(* M2Range.mod exports procedures which maintain the range checking. Copyright (C) 2008-2023 Free Software Foundation, Inc. Contributed by Gaius Mulley . This file is part of GNU Modula-2. GNU Modula-2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Modula-2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Modula-2; see the file COPYING3. If not see . *) IMPLEMENTATION MODULE M2Range ; FROM SymbolTable IMPORT NulSym, GetLowestType, PutReadQuad, RemoveReadQuad, IsVar, IsConst, PushValue, GetSubrange, GetType, IsSubrange, GetSymName, IsTemporary, IsSet, IsRecord, IsPointer, IsArray, IsProcType, IsConstLit, IsAModula2Type, IsUnbounded, IsEnumeration, GetMode, IsConstString, MakeConstLit, SkipType, IsProcedure, IsParameter, GetDeclaredMod, IsVarParam, GetNthParam, ModeOfAddr ; FROM m2tree IMPORT Tree, debug_tree ; FROM m2linemap IMPORT ErrorAt, GetFilenameFromLocation, GetColumnNoFromLocation, GetLineNoFromLocation ; FROM m2type IMPORT GetMinFrom, GetMaxFrom, GetIntegerType, GetTreeType, GetPointerType, AddStatement ; FROM m2statement IMPORT BuildProcedureCallTree, BuildIfThenElseEnd, BuildIfThenDoEnd ; FROM m2expr IMPORT CompareTrees, BuildSub, BuildAdd, GetIntegerZero, GetIntegerOne, BuildAddr, BuildIndirect, BuildGreaterThan, BuildLessThan, BuildGreaterThanOrEqual, GetPointerZero, BuildNegate, BuildEqualTo, BuildLessThanOrEqual, IsTrue, IsFalse, TreeOverflow ; FROM m2convert IMPORT BuildConvert ; FROM m2statement IMPORT BuildParam ; FROM m2decl IMPORT BuildStringConstant, BuildIntegerConstant ; FROM m2builtins IMPORT BuiltInIsfinite ; FROM M2Debug IMPORT Assert ; FROM Indexing IMPORT Index, InitIndex, InBounds, PutIndice, GetIndice ; FROM Storage IMPORT ALLOCATE ; FROM M2ALU IMPORT PushIntegerTree, PushInt, ConvertToInt, Equ, Gre, Less, GreEqu ; FROM M2Options IMPORT VariantValueChecking ; FROM M2Error IMPORT Error, InternalError, ErrorFormat0, ErrorFormat1, ErrorFormat2, FlushErrors, GetAnnounceScope ; FROM M2ColorString IMPORT quoteOpen, quoteClose ; FROM M2MetaError IMPORT MetaError1, MetaError2, MetaError3, MetaErrorT0, MetaErrorT1, MetaErrorT2, MetaErrorT3, MetaErrorsT1, MetaErrorsT2, MetaErrorsT3, MetaErrorsT4, MetaErrorStringT1, MetaErrorStringT2, MetaErrorStringT3, MetaString3 ; FROM M2LexBuf IMPORT UnknownTokenNo, GetTokenNo, FindFileNameFromToken, TokenToLineNo, TokenToColumnNo, TokenToLocation ; FROM StrIO IMPORT WriteString, WriteLn ; FROM M2GCCDeclare IMPORT TryDeclareConstant, DeclareConstructor ; FROM M2Quads IMPORT QuadOperator, PutQuad, SubQuad, WriteOperand ; FROM SymbolConversion IMPORT GccKnowsAbout, Mod2Gcc ; FROM Lists IMPORT List ; FROM NameKey IMPORT Name, MakeKey, KeyToCharStar ; FROM StdIO IMPORT Write ; FROM DynamicStrings IMPORT String, string, Length, InitString, ConCat, ConCatChar, Mark, InitStringCharStar, KillString ; FROM M2GenGCC IMPORT GetHighFromUnbounded, StringToChar, LValueToGenericPtr, ZConstToTypedConst ; FROM M2System IMPORT Address, Word, Loc, Byte, IsWordN, IsRealN, IsComplexN ; FROM FormatStrings IMPORT Sprintf0, Sprintf1, Sprintf2 ; FROM M2Check IMPORT ParameterTypeCompatible, ExpressionTypeCompatible ; FROM M2Base IMPORT Nil, IsRealType, GetBaseTypeMinMax, Cardinal, Integer, ZType, IsComplexType, IsAssignmentCompatible, IsExpressionCompatible, IsParameterCompatible, ExceptionAssign, ExceptionReturn, ExceptionInc, ExceptionDec, ExceptionIncl, ExceptionExcl, ExceptionShift, ExceptionRotate, ExceptionStaticArray, ExceptionDynamicArray, ExceptionForLoopBegin, ExceptionForLoopTo, ExceptionForLoopEnd, ExceptionPointerNil, ExceptionNoReturn, ExceptionCase, ExceptionNonPosDiv, ExceptionNonPosMod, ExceptionZeroDiv, ExceptionZeroRem, ExceptionWholeValue, ExceptionRealValue, ExceptionParameterBounds, ExceptionNo ; FROM M2CaseList IMPORT CaseBoundsResolved, OverlappingCaseBounds, WriteCase, MissingCaseBounds, TypeCaseBounds ; TYPE TypeOfRange = (assignment, returnassignment, subrangeassignment, inc, dec, incl, excl, shift, rotate, typeexpr, typeassign, typeparam, paramassign, staticarraysubscript, dynamicarraysubscript, forloopbegin, forloopto, forloopend, pointernil, noreturn, noelse, casebounds, wholenonposdiv, wholenonposmod, wholezerodiv, wholezerorem, none) ; Range = POINTER TO RECORD type : TypeOfRange ; des, expr, desLowestType, exprLowestType: CARDINAL ; procedure : CARDINAL ; paramNo : CARDINAL ; isLeftValue : BOOLEAN ; (* is des an LValue, only used in pointernil *) dimension : CARDINAL ; caseList : CARDINAL ; tokenNo : CARDINAL ; errorReported : BOOLEAN ; (* error message reported yet? *) strict : BOOLEAN ; (* is it a comparison expression? *) isin : BOOLEAN ; (* expression created by IN operator? *) END ; VAR TopOfRange: CARDINAL ; RangeIndex: Index ; (* OverlapsRange - returns TRUE if a1..a2 overlaps with b1..b2. *) PROCEDURE OverlapsRange (a1, a2, b1, b2: Tree) : BOOLEAN ; BEGIN (* RETURN( ((a1<=b2) AND (a2>=b1)) ) *) RETURN( (CompareTrees(a1, b2)<=0) AND (CompareTrees(a2, b1)>=0) ) END OverlapsRange ; (* IsGreater - returns TRUE if a>b. *) PROCEDURE IsGreater (a, b: Tree) : BOOLEAN ; BEGIN RETURN( CompareTrees(a, b)>0 ) END IsGreater ; (* IsGreaterOrEqual - returns TRUE if a>=b. *) PROCEDURE IsGreaterOrEqual (a, b: Tree) : BOOLEAN ; BEGIN RETURN( CompareTrees(a, b)>=0 ) END IsGreaterOrEqual ; (* IsEqual - returns TRUE if a=b. *) PROCEDURE IsEqual (a, b: Tree) : BOOLEAN ; BEGIN RETURN( CompareTrees(a, b)=0 ) END IsEqual ; (* IsGreaterOrEqualConversion - tests whether t>=e. *) PROCEDURE IsGreaterOrEqualConversion (location: location_t; l: CARDINAL; d, e: CARDINAL) : BOOLEAN ; BEGIN IF GetType(d)=NulSym THEN IF GetType(e)=NulSym THEN RETURN( IsGreaterOrEqual(Mod2Gcc(l), LValueToGenericPtr(location, e)) ) ELSE RETURN( IsGreaterOrEqual(BuildConvert(location, Mod2Gcc(SkipType(GetType(e))), Mod2Gcc(l), FALSE), LValueToGenericPtr(location, e)) ) END ELSE RETURN( IsGreaterOrEqual(BuildConvert(location, Mod2Gcc(SkipType(GetType(d))), Mod2Gcc(l), FALSE), LValueToGenericPtr(location, e)) ) END END IsGreaterOrEqualConversion ; (* IsEqualConversion - returns TRUE if a=b. *) PROCEDURE IsEqualConversion (l: CARDINAL; d, e: CARDINAL) : BOOLEAN ; VAR location: location_t ; BEGIN location := TokenToLocation(GetDeclaredMod(l)) ; IF GetType(d)=NulSym THEN IF GetType(e)=NulSym THEN RETURN( IsEqual(Mod2Gcc(l), LValueToGenericPtr(location, e)) ) ELSE RETURN( IsEqual(BuildConvert(location, Mod2Gcc(SkipType(GetType(e))), Mod2Gcc(l), FALSE), LValueToGenericPtr(location, e)) ) END ELSE RETURN( IsEqual(BuildConvert(location, Mod2Gcc(SkipType(GetType(d))), Mod2Gcc(l), FALSE), LValueToGenericPtr(location, e)) ) END END IsEqualConversion ; (* lookupExceptionHandler - *) PROCEDURE lookupExceptionHandler (type: TypeOfRange) : CARDINAL ; BEGIN CASE type OF assignment : RETURN( ExceptionAssign ) | returnassignment : RETURN( ExceptionReturn ) | subrangeassignment : InternalError ('not expecting this case value') | inc : RETURN( ExceptionInc ) | dec : RETURN( ExceptionDec ) | incl : RETURN( ExceptionIncl ) | excl : RETURN( ExceptionExcl ) | shift : RETURN( ExceptionShift ) | rotate : RETURN( ExceptionRotate ) | typeassign : InternalError ('not expecting this case value') | typeparam : InternalError ('not expecting this case value') | typeexpr : InternalError ('not expecting this case value') | paramassign : RETURN( ExceptionParameterBounds ) | staticarraysubscript : RETURN( ExceptionStaticArray ) | dynamicarraysubscript: RETURN( ExceptionDynamicArray ) | forloopbegin : RETURN( ExceptionForLoopBegin ) | forloopto : RETURN( ExceptionForLoopTo ) | forloopend : RETURN( ExceptionForLoopEnd ) | pointernil : RETURN( ExceptionPointerNil ) | noreturn : RETURN( ExceptionNoReturn ) | noelse : RETURN( ExceptionCase ) | casebounds : InternalError ('not expecting this case value') | wholenonposdiv : RETURN( ExceptionNonPosDiv ) | wholenonposmod : RETURN( ExceptionNonPosMod ) | wholezerodiv : RETURN( ExceptionZeroDiv ) | wholezerorem : RETURN( ExceptionZeroRem ) | none : RETURN( ExceptionNo ) ELSE InternalError ('enumeration value unknown') END END lookupExceptionHandler ; (* InitRange - returns a new range item. *) PROCEDURE InitRange () : CARDINAL ; VAR r: CARDINAL ; p: Range ; BEGIN INC(TopOfRange) ; r := TopOfRange ; NEW(p) ; IF p=NIL THEN InternalError ('out of memory error') ELSE WITH p^ DO type := none ; des := NulSym ; expr := NulSym ; desLowestType := NulSym ; exprLowestType := NulSym ; isLeftValue := FALSE ; (* ignored in all cases other *) dimension := 0 ; caseList := 0 ; tokenNo := 0 ; (* than pointernil *) errorReported := FALSE END ; PutIndice(RangeIndex, r, p) END ; RETURN( r ) END InitRange ; (* reportedError - returns whether this is the first time this error has been reported. *) PROCEDURE reportedError (r: CARDINAL) : BOOLEAN ; VAR p: Range ; BEGIN p := GetIndice (RangeIndex, r) ; RETURN p^.errorReported END reportedError ; (* setReported - assigns errorReported to TRUE. *) PROCEDURE setReported (r: CARDINAL) ; VAR p: Range ; BEGIN p := GetIndice (RangeIndex, r) ; p^.errorReported := TRUE END setReported ; (* PutRange - initializes contents of, p, to d, e and their lowest types. It also fills in the current token no and returns, p. *) PROCEDURE PutRange (tokno: CARDINAL; p: Range; t: TypeOfRange; d, e: CARDINAL) : Range ; BEGIN WITH p^ DO type := t ; des := d ; expr := e ; desLowestType := GetLowestType (d) ; exprLowestType := GetLowestType (e) ; tokenNo := tokno ; strict := FALSE ; isin := FALSE END ; RETURN p END PutRange ; (* chooseTokenPos - returns, tokenpos, if it is not the unknown location, otherwise it returns GetTokenNo. *) PROCEDURE chooseTokenPos (tokenpos: CARDINAL) : CARDINAL ; BEGIN IF tokenpos = UnknownTokenNo THEN RETURN GetTokenNo () ELSE RETURN tokenpos END END chooseTokenPos ; (* PutRangeNoLow - initializes contents of, p. It does not set lowest types as they may be unknown at this point. *) PROCEDURE PutRangeNoLow (tokpos: CARDINAL; p: Range; t: TypeOfRange; d, e: CARDINAL) : Range ; BEGIN WITH p^ DO type := t ; des := d ; expr := e ; desLowestType := NulSym ; exprLowestType := NulSym ; isLeftValue := FALSE ; tokenNo := chooseTokenPos (tokpos) ; strict := FALSE ; isin := FALSE END ; RETURN p END PutRangeNoLow ; (* PutRangeExpr - initializes contents of, p. It does not set lowest types as they may be unknown at this point. *) PROCEDURE PutRangeExpr (tokpos: CARDINAL; p: Range; t: TypeOfRange; d, e: CARDINAL; strict, isin: BOOLEAN) : Range ; BEGIN WITH p^ DO type := t ; des := d ; expr := e ; desLowestType := NulSym ; exprLowestType := NulSym ; isLeftValue := FALSE ; tokenNo := chooseTokenPos (tokpos) ; END ; p^.strict := strict ; p^.isin := isin ; RETURN p END PutRangeExpr ; (* PutRangePointer - initializes contents of, p, to d, isLeft and their lowest types. It also fills in the current token no and returns, p. *) PROCEDURE PutRangePointer (tokpos: CARDINAL; p: Range; d: CARDINAL; isLeft: BOOLEAN) : Range ; BEGIN WITH p^ DO type := pointernil ; des := d ; expr := NulSym ; desLowestType := GetLowestType(GetType(d)) ; exprLowestType := NulSym ; isLeftValue := isLeft ; tokenNo := tokpos ; strict := FALSE ; isin := FALSE END ; RETURN p END PutRangePointer ; (* PutRangeNoEval - initializes contents of, p, to a non evaluation runtime check such as a no else clause or no return found in function call. *) PROCEDURE PutRangeNoEval (p: Range; t: TypeOfRange) : Range ; BEGIN WITH p^ DO type := t ; tokenNo := GetTokenNo () END ; RETURN p END PutRangeNoEval ; (* PutRange - initializes contents of, p, to d, e and its lowest type. It also fills in the current token no and returns, p. *) PROCEDURE PutRangeUnary (tokno: CARDINAL; p: Range; t: TypeOfRange; d, e: CARDINAL) : Range ; BEGIN WITH p^ DO type := t ; des := d ; expr := e ; desLowestType := GetLowestType(d) ; exprLowestType := NulSym ; isLeftValue := FALSE ; tokenNo := chooseTokenPos (tokno) ; strict := FALSE ; isin := FALSE END ; RETURN( p ) END PutRangeUnary ; (* PutRangeParam - initializes contents of, p, to contain the parameter type checking information. It also fills in the current token no and returns, p. *) PROCEDURE PutRangeParam (p: Range; t: TypeOfRange; proc: CARDINAL; i: CARDINAL; formal, actual: CARDINAL) : Range ; BEGIN WITH p^ DO type := t ; des := formal ; expr := actual ; desLowestType := NulSym ; exprLowestType := NulSym ; procedure := proc ; paramNo := i ; isLeftValue := FALSE ; tokenNo := GetTokenNo () ; strict := FALSE ; isin := FALSE END ; RETURN p END PutRangeParam ; (* PutRangeArraySubscript - initializes contents of, p, to d, e and their lowest types. It also assigns, dim. It also fills in the current token no and returns, p. *) PROCEDURE PutRangeArraySubscript (p: Range; t: TypeOfRange; d, e: CARDINAL; dim: CARDINAL) : Range ; BEGIN WITH p^ DO type := t ; des := d ; expr := e ; desLowestType := GetLowestType(d) ; exprLowestType := GetLowestType(e) ; dimension := dim ; tokenNo := GetTokenNo () ; strict := FALSE ; isin := FALSE END ; RETURN p END PutRangeArraySubscript ; (* InitAssignmentRangeCheck - returns a range check node which remembers the information necessary so that a range check for d := e can be generated later on. *) PROCEDURE InitAssignmentRangeCheck (tokno: CARDINAL; d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRange (tokno, GetIndice (RangeIndex, r), assignment, d, e) # NIL) ; RETURN r END InitAssignmentRangeCheck ; (* InitReturnRangeCheck - returns a range check node which remembers the information necessary so that a range check for RETURN e from procedure, d, can be generated later on. *) PROCEDURE InitReturnRangeCheck (tokno: CARDINAL; d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRange (tokno, GetIndice (RangeIndex, r), returnassignment, d, e) # NIL) ; RETURN r END InitReturnRangeCheck ; (* InitSubrangeRangeCheck - returns a range check node which remembers the information necessary so that a range check for d := e can be generated later on. *) PROCEDURE InitSubrangeRangeCheck (d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRange (GetTokenNo (), GetIndice (RangeIndex, r), subrangeassignment, d, e) # NIL) ; RETURN r END InitSubrangeRangeCheck ; (* InitStaticArraySubscriptRangeCheck - returns a range check node which remembers the information necessary so that a range check for d[e] can be generated later on. *) PROCEDURE InitStaticArraySubscriptRangeCheck (d, e, dim: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangeArraySubscript (GetIndice (RangeIndex, r), staticarraysubscript, d, e, dim) # NIL) ; RETURN r END InitStaticArraySubscriptRangeCheck ; (* InitDynamicArraySubscriptRangeCheck - returns a range check node which remembers the information necessary so that a range check for d[e] can be generated later on. *) PROCEDURE InitDynamicArraySubscriptRangeCheck (d, e, dim: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangeArraySubscript (GetIndice (RangeIndex, r), dynamicarraysubscript, d, e, dim) # NIL) ; RETURN r END InitDynamicArraySubscriptRangeCheck ; (* InitIncRangeCheck - returns a range check node which remembers the information necessary so that a range check for INC(d, e) can be generated later on. *) PROCEDURE InitIncRangeCheck (d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRange (GetTokenNo (), GetIndice (RangeIndex, r), inc, d, e) # NIL) ; RETURN r END InitIncRangeCheck ; (* InitDecRangeCheck - returns a range check node which remembers the information necessary so that a range check for DEC(d, e) can be generated later on. *) PROCEDURE InitDecRangeCheck (d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRange (GetTokenNo (), GetIndice (RangeIndex, r), dec, d, e) # NIL) ; RETURN r END InitDecRangeCheck ; (* InitInclCheck - checks to see that bit, e, is type compatible with e and also in range. *) PROCEDURE InitInclCheck (d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangeNoLow (GetTokenNo (), GetIndice (RangeIndex, r), incl, d, e) # NIL) ; RETURN r END InitInclCheck ; (* InitExclCheck - checks to see that bit, e, is type compatible with e and also in range. *) PROCEDURE InitExclCheck (d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangeNoLow (GetTokenNo (), GetIndice (RangeIndex, r), excl, d, e) # NIL) ; RETURN r END InitExclCheck ; (* InitShiftCheck - checks to see that bit, e, is type compatible with d and also in range. *) PROCEDURE InitShiftCheck (d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangeNoLow (GetTokenNo (), GetIndice (RangeIndex, r), shift, d, e) # NIL) ; RETURN r END InitShiftCheck ; (* InitRotateCheck - checks to see that bit, e, is type compatible with d and also in range. *) PROCEDURE InitRotateCheck (d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangeNoLow (GetTokenNo (), GetIndice (RangeIndex, r), rotate, d, e) # NIL) ; RETURN r END InitRotateCheck ; (* InitTypesAssignmentCheck - checks to see that the types of, d, and, e, are assignment compatible. *) PROCEDURE InitTypesAssignmentCheck (tokno: CARDINAL; d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangeNoLow (tokno, GetIndice (RangeIndex, r), typeassign, d, e) # NIL) ; RETURN r END InitTypesAssignmentCheck ; (* InitTypesParameterCheck - checks to see that the types of, d, and, e, are parameter compatible. *) PROCEDURE InitTypesParameterCheck (proc: CARDINAL; i: CARDINAL; formal, actual: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangeParam (GetIndice (RangeIndex, r), typeparam, proc, i, formal, actual) # NIL) ; RETURN r END InitTypesParameterCheck ; (* PutRangeParamAssign - initializes contents of, p, to contain the parameter type checking information. It also fills in the current token no and returns, p. *) PROCEDURE PutRangeParamAssign (p: Range; t: TypeOfRange; proc: CARDINAL; i: CARDINAL; formal, actual: CARDINAL) : Range ; BEGIN WITH p^ DO type := t ; des := formal ; expr := actual ; desLowestType := GetLowestType (des) ; exprLowestType := GetLowestType (expr) ; procedure := proc ; paramNo := i ; dimension := i ; isLeftValue := FALSE ; tokenNo := GetTokenNo () END ; RETURN( p ) END PutRangeParamAssign ; (* InitParameterRangeCheck - checks to see that the types of, d, and, e, are parameter compatible. *) PROCEDURE InitParameterRangeCheck (proc: CARDINAL; i: CARDINAL; formal, actual: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangeParamAssign (GetIndice (RangeIndex, r), paramassign, proc, i, formal, actual) # NIL) ; RETURN r END InitParameterRangeCheck ; (* InitTypesExpressionCheck - checks to see that the types of, d, and, e, are expression compatible. *) PROCEDURE InitTypesExpressionCheck (tokno: CARDINAL; d, e: CARDINAL; strict, isin: BOOLEAN) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange() ; Assert (PutRangeExpr (tokno, GetIndice (RangeIndex, r), typeexpr, d, e, strict, isin) # NIL) ; RETURN r END InitTypesExpressionCheck ; (* InitForLoopBeginRangeCheck - returns a range check node which remembers the information necessary so that a range check for FOR d := e TO .. DO can be generated later on. *) PROCEDURE InitForLoopBeginRangeCheck (d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRange (GetTokenNo (), GetIndice (RangeIndex, r), forloopbegin, d, e) # NIL) ; RETURN r END InitForLoopBeginRangeCheck ; (* InitForLoopToRangeCheck - returns a range check node which remembers the information necessary so that a range check for FOR d := e TO .. DO can be generated later on. *) PROCEDURE InitForLoopToRangeCheck (d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRange (GetTokenNo (), GetIndice (RangeIndex, r), forloopto, d, e) # NIL) ; RETURN r END InitForLoopToRangeCheck ; (* InitForLoopEndRangeCheck - returns a range check node which remembers the information necessary so that a range check for INC or DEC(d, e) can be generated later on. *) PROCEDURE InitForLoopEndRangeCheck (d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRange (GetTokenNo (), GetIndice (RangeIndex, r), forloopend, d, e) # NIL) ; RETURN r END InitForLoopEndRangeCheck ; (* InitPointerRangeCheck - creates a pointer # NIL check. *) PROCEDURE InitPointerRangeCheck (tokno: CARDINAL; d: CARDINAL; isLeft: BOOLEAN) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangePointer (tokno, GetIndice (RangeIndex, r), d, isLeft) # NIL) ; RETURN r END InitPointerRangeCheck ; (* InitNoReturnRangeCheck - creates a check held in the function to detect the absence of a RETURN statement at runtime. *) PROCEDURE InitNoReturnRangeCheck () : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangeNoEval (GetIndice(RangeIndex, r), noreturn) # NIL) ; RETURN r END InitNoReturnRangeCheck ; (* InitNoElseRangeCheck - creates a check held at the end of a CASE statement without an ELSE clause to detect its absence at runtime. *) PROCEDURE InitNoElseRangeCheck () : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangeNoEval (GetIndice (RangeIndex, r), noelse) # NIL) ; RETURN r END InitNoElseRangeCheck ; (* InitWholeNonPosDivCheck - creates a check expression for non positive or zero 2nd operand to division. *) PROCEDURE InitWholeNonPosDivCheck (tokno: CARDINAL; d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangeUnary (tokno, GetIndice (RangeIndex, r), wholenonposdiv, d, e) # NIL) ; RETURN r END InitWholeNonPosDivCheck ; (* InitWholeNonPosModCheck - creates a check expression for non positive or zero 2nd operand to modulus. *) PROCEDURE InitWholeNonPosModCheck (tokno: CARDINAL; d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangeUnary (tokno, GetIndice (RangeIndex, r), wholenonposmod, d, e) # NIL) ; RETURN r END InitWholeNonPosModCheck ; (* InitWholeZeroDivisionCheck - creates a check expression for zero 2nd operand for division. *) PROCEDURE InitWholeZeroDivisionCheck (tokno: CARDINAL; d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangeUnary (tokno, GetIndice (RangeIndex, r), wholezerodiv, d, e) # NIL) ; RETURN r END InitWholeZeroDivisionCheck ; (* InitWholeZeroRemainderCheck - creates a check expression for zero 2nd operand for remainder. *) PROCEDURE InitWholeZeroRemainderCheck (tokno: CARDINAL; d, e: CARDINAL) : CARDINAL ; VAR r: CARDINAL ; BEGIN r := InitRange () ; Assert (PutRangeUnary (tokno, GetIndice (RangeIndex, r), wholezerorem, d, e) # NIL) ; RETURN r END InitWholeZeroRemainderCheck ; (* FoldNil - attempts to fold the pointer against nil comparison. *) PROCEDURE FoldNil (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p: Range ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant (tokenno, des) ; (* use quad tokenno, rather than the range tokenNo *) IF GccKnowsAbout (des) AND IsConst (des) THEN PushValue (des) ; PushValue (Nil) ; IF Equ (tokenno) THEN MetaErrorT1 (tokenNo, 'attempting to dereference a pointer {%1Wa} whose value will be NIL', des) ; PutQuad (q, ErrorOp, NulSym, NulSym, r) ELSE SubQuad (q) END END END END FoldNil ; (* GetMinMax - returns TRUE if we know the max and min of m2type. *) PROCEDURE GetMinMax (tokenno: CARDINAL; type: CARDINAL; VAR min, max: Tree) : BOOLEAN ; VAR minC, maxC: CARDINAL ; location : location_t ; BEGIN location := TokenToLocation(tokenno) ; Assert (IsAModula2Type (type)) ; IF GccKnowsAbout(type) AND (NOT IsPointer(type)) AND (NOT IsArray(type)) AND (NOT IsRecord(type)) AND (NOT IsRecord(type)) AND (NOT IsUnbounded(type)) AND (NOT IsProcType(type)) AND (NOT IsRealType(type)) AND (NOT IsRealN(type)) AND (NOT IsComplexType(type)) AND (NOT IsComplexN(type)) AND (type#Address) AND (NOT IsSet(type)) AND (type#Word) AND (type#Loc) AND (type#Byte) AND (NOT IsWordN(type)) THEN IF IsSubrange(type) THEN GetSubrange(type, maxC, minC) ; max := Mod2Gcc(maxC) ; min := Mod2Gcc(minC) ELSIF IsEnumeration(type) THEN GetBaseTypeMinMax(type, minC, maxC) ; max := Mod2Gcc(maxC) ; min := Mod2Gcc(minC) ELSE max := GetMaxFrom(location, Mod2Gcc(type)) ; min := GetMinFrom(location, Mod2Gcc(type)) END ; max := BuildConvert (location, Mod2Gcc(type), max, FALSE) ; Assert (NOT TreeOverflow (max)) ; min := BuildConvert (location, Mod2Gcc(type), min, FALSE) ; Assert (NOT TreeOverflow (min)) ; RETURN TRUE ELSE RETURN FALSE END END GetMinMax ; (* OutOfRange - returns TRUE if expr lies outside min..max. *) PROCEDURE OutOfRange (tokenno: CARDINAL; min: Tree; expr: CARDINAL; max: Tree; type: CARDINAL) : BOOLEAN ; BEGIN IF TreeOverflow (min) THEN WriteString ("overflow detected in min\n"); WriteLn ; debug_tree (min) END ; IF TreeOverflow (max) THEN WriteString ("overflow detected in max\n"); WriteLn ; debug_tree (max) END ; IF TreeOverflow (max) THEN WriteString ("overflow detected in expr\n"); WriteLn ; debug_tree (StringToChar (Mod2Gcc (expr), type, expr)); END ; PushIntegerTree (StringToChar (Mod2Gcc (expr), type, expr)) ; PushIntegerTree (min) ; IF Less (tokenno) THEN RETURN TRUE END ; PushIntegerTree (StringToChar (Mod2Gcc (expr), type, expr)) ; PushIntegerTree (max) ; IF Gre (tokenno) THEN RETURN TRUE END ; RETURN FALSE END OutOfRange ; (* HandlerExists - *) PROCEDURE HandlerExists (r: CARDINAL) : BOOLEAN ; VAR p: Range ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO CASE type OF assignment : RETURN( ExceptionAssign#NulSym ) | returnassignment : RETURN( ExceptionReturn#NulSym ) | subrangeassignment : InternalError ('not expecting this case value') | inc : RETURN( ExceptionInc#NulSym ) | dec : RETURN( ExceptionDec#NulSym ) | incl : RETURN( ExceptionIncl#NulSym ) | excl : RETURN( ExceptionExcl#NulSym ) | shift : RETURN( ExceptionShift#NulSym ) | rotate : RETURN( ExceptionRotate#NulSym ) | typeassign : RETURN( FALSE ) | typeparam : RETURN( FALSE ) | typeexpr : RETURN( FALSE ) | paramassign : RETURN( ExceptionParameterBounds#NulSym ) | staticarraysubscript : RETURN( ExceptionStaticArray#NulSym ) | dynamicarraysubscript: RETURN( ExceptionDynamicArray#NulSym ) | forloopbegin : RETURN( ExceptionForLoopBegin#NulSym ) | forloopto : RETURN( ExceptionForLoopTo#NulSym ) | forloopend : RETURN( ExceptionForLoopEnd#NulSym ) | pointernil : RETURN( ExceptionPointerNil#NulSym ) | noreturn : RETURN( ExceptionNoReturn#NulSym ) | noelse : RETURN( ExceptionCase#NulSym ) | casebounds : RETURN( FALSE ) | wholenonposdiv : RETURN( ExceptionNonPosDiv#NulSym ) | wholenonposmod : RETURN( ExceptionNonPosMod#NulSym ) | wholezerodiv : RETURN( ExceptionZeroDiv#NulSym ) | wholezerorem : RETURN( ExceptionZeroRem#NulSym ) | none : RETURN( FALSE ) ELSE InternalError ('enumeration value unknown') END END END HandlerExists ; (* FoldAssignment - *) PROCEDURE FoldAssignment (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p : Range ; min, max: Tree ; BEGIN p := GetIndice (RangeIndex, r) ; WITH p^ DO TryDeclareConstant (tokenNo, expr) ; IF desLowestType # NulSym THEN IF GccKnowsAbout (expr) AND IsConst (expr) AND GetMinMax (tokenno, desLowestType, min, max) THEN 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 END END END FoldAssignment ; (* FoldParameterAssign - *) PROCEDURE FoldParameterAssign (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p : Range ; min, max: Tree ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant (tokenNo, expr) ; IF desLowestType#NulSym THEN IF GccKnowsAbout(expr) AND IsConst(expr) AND GetMinMax(tokenno, desLowestType, min, max) THEN IF OutOfRange(tokenno, min, expr, max, desLowestType) THEN (* this is safer to treat as an error, rather than a warning otherwise the paramater might be widened (if it is a constant). *) MetaErrorT3(tokenNo, 'the {%3EN} actual parameter {%2a} will exceed the range of formal parameter type {%1tad}', des, expr, dimension) ; PutQuad(q, ErrorOp, NulSym, NulSym, r) ELSE SubQuad(q) END END END END END FoldParameterAssign ; (* FoldReturn - do we know this is reachable, if so generate an error message. *) PROCEDURE FoldReturn (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p : Range ; min, max: Tree ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant (tokenNo, expr) ; IF desLowestType#NulSym THEN IF GccKnowsAbout(expr) AND IsConst(expr) AND GetMinMax(tokenno, desLowestType, min, max) THEN IF OutOfRange(tokenno, min, expr, max, desLowestType) THEN MetaErrorT2(tokenNo, 'attempting to return {%2Wa} from a procedure function {%1a} which will exceed exceed the range of type {%1tad}', des, expr) ; PutQuad(q, ErrorOp, NulSym, NulSym, r) ELSE SubQuad(q) END END END END END FoldReturn ; (* FoldInc - *) PROCEDURE FoldInc (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p : Range ; t, min, max: Tree ; location : location_t ; BEGIN location := TokenToLocation(tokenno) ; p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, des) ; (* use quad tokenno, rather than the range tokenNo *) TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) IF desLowestType#NulSym THEN IF GccKnowsAbout(expr) AND IsConst(expr) AND GetMinMax(tokenno, desLowestType, min, max) THEN IF OutOfRange(tokenno, GetIntegerZero(location), expr, max, desLowestType) THEN MetaErrorT2(tokenNo, 'operand to INC {%2Wa} exceeds the range of type {%1ts} of the designator {%1a}', des, expr) ; PutQuad(q, ErrorOp, NulSym, NulSym, r) ELSIF GccKnowsAbout(des) AND IsConst(des) AND GccKnowsAbout(desLowestType) THEN t := BuildSub(location, max, BuildConvert(location, Mod2Gcc(desLowestType), Mod2Gcc(expr), FALSE), FALSE) ; PushIntegerTree(Mod2Gcc(des)) ; PushIntegerTree(t) ; IF Gre(tokenNo) THEN MetaErrorT1(tokenNo, 'the designator to INC {%1Wa} will exceed the range of type {%1ts}', des) ; PutQuad(q, ErrorOp, NulSym, NulSym, r) ELSE (* range check is unnecessary *) SubQuad(q) END END END END END END FoldInc ; (* FoldDec - *) PROCEDURE FoldDec (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p : Range ; t, min, max: Tree ; location : location_t ; BEGIN location := TokenToLocation(tokenno) ; p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, des) ; (* use quad tokenno, rather than the range tokenNo *) TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) IF desLowestType#NulSym THEN IF GccKnowsAbout(expr) AND IsConst(expr) AND GetMinMax(tokenno, desLowestType, min, max) THEN IF OutOfRange(tokenno, GetIntegerZero(location), expr, max, desLowestType) THEN MetaErrorT2(tokenNo, 'operand to DEC {%2Wa} exceeds the range of type {%1ts} of the designator {%1a}', des, expr) ; PutQuad(q, ErrorOp, NulSym, NulSym, r) ELSIF GccKnowsAbout(des) AND IsConst(des) AND GccKnowsAbout(desLowestType) THEN t := BuildSub(location, BuildConvert(location, Mod2Gcc(desLowestType), Mod2Gcc(expr), FALSE), min, FALSE) ; PushIntegerTree(Mod2Gcc(des)) ; PushIntegerTree(t) ; IF Less(tokenNo) THEN MetaErrorT1(tokenNo, 'the designator to DEC {%1Wa} will exceed the range of type {%1ts}', des) ; PutQuad(q, ErrorOp, NulSym, NulSym, r) ELSE (* range check is unnecessary *) SubQuad(q) END END END END END END FoldDec ; (* CheckSetAndBit - returns TRUE if des is a set type and expr is compatible with des. *) PROCEDURE CheckSetAndBit (tokenno: CARDINAL; des, expr: CARDINAL; name: ARRAY OF CHAR) : BOOLEAN ; VAR s: String ; BEGIN IF IsSet(des) THEN IF IsExpressionCompatible(GetType(des), GetType(expr)) THEN RETURN( TRUE ) ELSE s := ConCat(ConCat(InitString('operands to '), Mark(InitString(name))), Mark(InitString(' {%1Etsd:{%2tsd:{%1tsd} and {%2tsd}}} are incompatible'))) ; MetaErrorStringT2(tokenno, s, des, expr) ; FlushErrors END ELSE s := ConCat(ConCat(InitString('first operand to '), Mark(InitString(name))), Mark(InitString(' is not a set {%1Etasd}'))) ; MetaErrorStringT1(tokenno, s, des) ; FlushErrors END ; RETURN( FALSE ) END CheckSetAndBit ; (* CheckSet - returns TRUE if des is a set type and expr is compatible with INTEGER. *) PROCEDURE CheckSet (tokenno: CARDINAL; des, expr: CARDINAL; name: ARRAY OF CHAR) : BOOLEAN ; VAR s: String ; BEGIN IF IsSet(des) THEN IF IsParameterCompatible(Integer, GetType(expr)) THEN RETURN( TRUE ) ELSE s := ConCat(ConCat(InitString('operands to '), Mark(InitString(name))), Mark(InitString(' {%1Etsd:{%2tsd:{%1tsd} and {%2tsd}}} are incompatible'))) ; MetaErrorStringT2(tokenno, s, des, expr) ; FlushErrors END ELSE s := ConCat(ConCat(InitString('first operand to '), Mark(InitString(name))), Mark(InitString(' is not a set {%1Etasd}'))) ; MetaErrorStringT1(tokenno, s, des) ; FlushErrors END ; RETURN( FALSE ) END CheckSet ; (* FoldIncl - folds an INCL statement if the operands are constant. *) PROCEDURE FoldIncl (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p : Range ; min, max: Tree ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, des) ; (* use quad tokenno, rather than the range tokenNo *) TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) desLowestType := SkipType(GetType(des)) ; IF desLowestType#NulSym THEN IF CheckSetAndBit(tokenno, desLowestType, expr, "INCL") THEN IF GccKnowsAbout(expr) AND IsConst(expr) AND GetMinMax(tokenno, desLowestType, min, max) THEN IF OutOfRange(tokenno, min, expr, max, desLowestType) THEN MetaErrorT2(tokenNo, 'operand to INCL {%2Wa} exceeds the range of type {%1tasa}', des, expr) ; PutQuad(q, ErrorOp, NulSym, NulSym, r) ELSE (* range check is unnecessary *) SubQuad(q) END END END END END END FoldIncl ; (* FoldExcl - folds an EXCL statement if the operands are constant. *) PROCEDURE FoldExcl (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p : Range ; min, max: Tree ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, des) ; (* use quad tokenno, rather than the range tokenNo *) TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) desLowestType := SkipType(GetType(des)) ; IF desLowestType#NulSym THEN IF CheckSetAndBit(tokenno, desLowestType, expr, "EXCL") THEN IF GccKnowsAbout(expr) AND IsConst(expr) AND GetMinMax(tokenno, desLowestType, min, max) THEN IF OutOfRange(tokenno, min, expr, max, desLowestType) THEN MetaErrorT2(tokenNo, 'operand to EXCL {%2Wa} exceeds the range of type {%1tasa}', des, expr) ; PutQuad(q, ErrorOp, NulSym, NulSym, r) ELSE (* range check is unnecessary *) SubQuad(q) END END END END END END FoldExcl ; (* FoldShift - folds an SHIFT test statement if the operands are constant. *) PROCEDURE FoldShift (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR ofType : CARDINAL ; p : Range ; shiftMin, shiftMax, min, max: Tree ; location : location_t ; BEGIN location := TokenToLocation(tokenno) ; p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, des) ; (* use quad tokenno, rather than the range tokenNo *) TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) desLowestType := SkipType(GetType(des)) ; IF desLowestType#NulSym THEN IF CheckSet(tokenno, desLowestType, expr, "SHIFT") THEN ofType := SkipType(GetType(desLowestType)) ; IF GccKnowsAbout(ofType) AND GccKnowsAbout(expr) AND IsConst(expr) AND GetMinMax(tokenno, ofType, min, max) THEN min := BuildConvert(location, GetIntegerType(), min, FALSE) ; max := BuildConvert(location, GetIntegerType(), max, FALSE) ; shiftMax := BuildAdd(location, BuildSub(location, max, min, FALSE), GetIntegerOne(location), FALSE) ; shiftMin := BuildNegate(location, shiftMax, FALSE) ; IF OutOfRange(tokenno, shiftMin, expr, shiftMax, desLowestType) THEN MetaErrorT2(tokenNo, 'operand to SHIFT {%2Wa} exceeds the range of type {%1tasa}', des, expr) ; PutQuad(q, ErrorOp, NulSym, NulSym, r) ELSE (* range check is unnecessary *) SubQuad(q) END END END END END END FoldShift ; (* FoldRotate - folds a ROTATE test statement if the operands are constant. *) PROCEDURE FoldRotate (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR ofType : CARDINAL ; p : Range ; rotateMin, rotateMax, min, max : Tree ; location : location_t ; BEGIN location := TokenToLocation(tokenno) ; p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, des) ; (* use quad tokenno, rather than the range tokenNo *) TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) desLowestType := SkipType(GetType(des)) ; IF desLowestType#NulSym THEN IF CheckSet(tokenno, desLowestType, expr, "ROTATE") THEN ofType := SkipType(GetType(desLowestType)) ; IF GccKnowsAbout(ofType) AND GccKnowsAbout(expr) AND IsConst(expr) AND GetMinMax(tokenno, ofType, min, max) THEN min := BuildConvert(location, GetIntegerType(), min, FALSE) ; max := BuildConvert(location, GetIntegerType(), max, FALSE) ; rotateMax := BuildAdd(location, BuildSub(location, max, min, FALSE), GetIntegerOne(location), FALSE) ; rotateMin := BuildNegate(location, rotateMax, FALSE) ; IF OutOfRange(tokenno, rotateMin, expr, rotateMax, desLowestType) THEN MetaErrorT2(tokenNo, 'operand to ROTATE {%2Wa} exceeds the range of type {%1tasa}', des, expr) ; PutQuad(q, ErrorOp, NulSym, NulSym, r) ELSE (* range check is unnecessary *) SubQuad(q) END END END END END END FoldRotate ; (* FoldTypeAssign - *) PROCEDURE FoldTypeAssign (q: CARDINAL; tokenNo: CARDINAL; des, expr: CARDINAL; r: CARDINAL) ; VAR exprType: CARDINAL ; BEGIN IF IsProcedure(expr) THEN exprType := expr ELSE exprType := GetType(expr) END ; IF IsAssignmentCompatible (GetType(des), exprType) THEN SubQuad(q) ELSE IF NOT reportedError (r) THEN IF IsProcedure (des) THEN MetaErrorsT2 (tokenNo, 'the return type {%1Etad} declared in procedure {%1Da}', 'is incompatible with the returned expression {%2ad}}', des, expr) ; ELSE MetaErrorT3 (tokenNo, 'assignment designator {%1Ea} {%1ta:of type {%1ta}} {%1d:is a {%1d}} and expression {%2a} {%3ad:of type {%3ad}} are incompatible', des, expr, exprType) END ; setReported (r) ; FlushErrors END END END FoldTypeAssign ; (* FoldTypeParam - *) PROCEDURE FoldTypeParam (q: CARDINAL; tokenNo: CARDINAL; formal, actual, procedure: CARDINAL; paramNo: CARDINAL) ; BEGIN IF ParameterTypeCompatible (tokenNo, '{%4EN} type failure between actual {%3ad} and the {%2ad}', procedure, formal, actual, paramNo, IsVarParam (procedure, paramNo)) THEN SubQuad(q) END END FoldTypeParam ; (* FoldTypeExpr - *) PROCEDURE FoldTypeExpr (q: CARDINAL; tokenNo: CARDINAL; left, right: CARDINAL; strict, isin: BOOLEAN; r: CARDINAL) ; BEGIN IF (left # NulSym) AND (right # NulSym) AND (NOT reportedError (r)) THEN IF ExpressionTypeCompatible (tokenNo, 'expression of type {%1Etad} is incompatible with type {%2tad}', left, right, strict, isin) THEN SubQuad(q) ; setReported (r) END END END FoldTypeExpr ; (* CodeTypeAssign - *) PROCEDURE CodeTypeAssign (tokenNo: CARDINAL; des, expr: CARDINAL; r: CARDINAL) ; VAR exprType: CARDINAL ; BEGIN IF IsProcedure(expr) THEN exprType := expr ELSE exprType := GetType(expr) END ; IF NOT IsAssignmentCompatible(GetType(des), exprType) THEN IF NOT reportedError (r) THEN IF IsProcedure(des) THEN MetaErrorsT2(tokenNo, 'the return type {%1Etad} declared in procedure {%1Da}', 'is incompatible with the returned expression {%2EUa} {%2tad:of type {%2tad}}', des, expr) ; ELSE MetaErrorT2(tokenNo, 'assignment designator {%1Ea} {%1ta:of type {%1ta}} {%1d:is a {%1d}} and expression {%2a} {%2tad:of type {%2tad}} are incompatible', des, expr) END ; setReported (r) END (* FlushErrors *) END END CodeTypeAssign ; (* CodeTypeParam - *) PROCEDURE CodeTypeParam (tokenNo: CARDINAL; formal, actual, procedure: CARDINAL; paramNo: CARDINAL) ; BEGIN IF NOT ParameterTypeCompatible (tokenNo, '{%4EN} type failure between actual {%3ad} and the formal {%2ad}', procedure, formal, actual, paramNo, IsVarParam (procedure, paramNo)) THEN END END CodeTypeParam ; (* CodeTypeExpr - *) PROCEDURE CodeTypeExpr (tokenNo: CARDINAL; left, right: CARDINAL; strict, isin: BOOLEAN; r: CARDINAL) ; BEGIN IF NOT reportedError (r) THEN IF ExpressionTypeCompatible (tokenNo, 'expression of type {%1Etad} is incompatible with type {%2tad}', left, right, strict, isin) THEN setReported (r) END END END CodeTypeExpr ; (* FoldTypeCheck - folds a type check. This is a no-op and it used for checking types which are resolved post pass 3. *) PROCEDURE FoldTypeCheck (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p: Range ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, des) ; (* use quad tokenno, rather than the range tokenNo *) TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) (* TryDeclareConstructor(q, expr) ; *) IF (GccKnowsAbout(des) OR (IsParameter(des) AND GccKnowsAbout(GetType(des)))) AND GccKnowsAbout(expr) THEN CASE type OF typeassign: FoldTypeAssign(q, tokenNo, des, expr, r) | typeparam: FoldTypeParam(q, tokenNo, des, expr, procedure, paramNo) | typeexpr: FoldTypeExpr(q, tokenNo, des, expr, strict, isin, r) ELSE InternalError ('not expecting to reach this point') END END END END FoldTypeCheck ; (* CodeTypeCheck - folds a type check. This is a no-op and it used for checking types which are resolved post pass 3. It does assume that both, des, and, expr, have been resolved at this point. *) PROCEDURE CodeTypeCheck (tokenno: CARDINAL; r: CARDINAL) ; VAR p: Range ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, des) ; (* use quad tokenno, rather than the range tokenNo *) TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) (* TryDeclareConstructor(0, expr) ; *) IF (GccKnowsAbout(des) OR (IsParameter(des) AND GccKnowsAbout(GetType(des)))) AND GccKnowsAbout(expr) THEN CASE type OF typeassign: CodeTypeAssign(tokenNo, des, expr, r) | typeparam: CodeTypeParam(tokenNo, des, expr, procedure, paramNo) | typeexpr: CodeTypeExpr(tokenNo, des, expr, strict, isin, r) ELSE InternalError ('not expecting to reach this point') END ELSE InternalError ('expecting des and expr to be resolved') END END END CodeTypeCheck ; (* FoldForLoopBegin - *) PROCEDURE FoldForLoopBegin (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p : Range ; min, max: Tree ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) IF desLowestType#NulSym THEN IF GccKnowsAbout(expr) AND IsConst(expr) AND GetMinMax(tokenno, desLowestType, min, max) THEN IF OutOfRange(tokenno, min, expr, max, desLowestType) THEN MetaErrorT2(tokenNo, 'attempting to assign a value {%2Wa} to a FOR loop designator {%1a} which will exceed the range of type {%1tad}', des, expr) ; PutQuad(q, ErrorOp, NulSym, NulSym, r) ELSE SubQuad(q) END END END END END FoldForLoopBegin ; (* FoldForLoopTo - *) PROCEDURE FoldForLoopTo (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p : Range ; min, max: Tree ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) IF desLowestType#NulSym THEN IF GccKnowsAbout(expr) AND IsConst(expr) AND GetMinMax(tokenno, desLowestType, min, max) THEN IF OutOfRange(tokenno, min, expr, max, desLowestType) THEN MetaErrorT2(tokenNo, 'final value in FOR loop will exceed type range {%1Wtasa} of designator {%2a}', des, expr) ; PutQuad(q, ErrorOp, NulSym, NulSym, r) ELSE SubQuad(q) END END END END END FoldForLoopTo ; (* FoldStaticArraySubscript - *) PROCEDURE FoldStaticArraySubscript (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p : Range ; min, max: Tree ; BEGIN p := GetIndice (RangeIndex, r) ; WITH p^ DO TryDeclareConstant (tokenno, des) ; (* use quad tokenno, rather than the range tokenNo *) TryDeclareConstant (tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) IF desLowestType#NulSym THEN IF GccKnowsAbout (expr) AND IsConst (expr) AND GetMinMax (tokenno, desLowestType, min, max) THEN IF OutOfRange (tokenno, min, expr, max, desLowestType) THEN MetaErrorT3 (tokenNo, 'index {%2Wa} out of range found while attempting to access an element of a static array {%1a} in the {%3N} array subscript', des, expr, dimension) ; PutQuad (q, ErrorOp, NulSym, NulSym, r) ELSE (* range check is unnecessary *) SubQuad (q) END END END END END FoldStaticArraySubscript ; (* FoldDynamicArraySubscript - *) PROCEDURE FoldDynamicArraySubscript (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p : Range ; location: location_t ; BEGIN location := TokenToLocation(tokenno) ; p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) IF desLowestType#NulSym THEN IF GccKnowsAbout(expr) AND IsConst(expr) THEN IF IsGreater(GetIntegerZero(location), BuildConvert(location, GetIntegerType(), Mod2Gcc(expr), FALSE)) THEN MetaErrorT3(tokenNo, 'index {%2Wa} out of range found while attempting to access an element of a dynamic array {%1a} in the {%3N} array subscript', des, expr, dimension) ; PutQuad(q, ErrorOp, NulSym, NulSym, r) ELSE (* cannot fold high bounds, so leave that for the runtime *) END END END END END FoldDynamicArraySubscript ; (* FoldCaseBounds - *) PROCEDURE FoldCaseBounds (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p: Range ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO IF CaseBoundsResolved(tokenno, caseList) THEN IF TypeCaseBounds (caseList) THEN (* nothing to do *) END ; IF OverlappingCaseBounds(caseList) THEN PutQuad(q, ErrorOp, NulSym, NulSym, r) ; IF VariantValueChecking AND MissingCaseBounds(tokenno, caseList) THEN (* nothing to do *) END ELSIF VariantValueChecking AND MissingCaseBounds(tokenno, caseList) THEN PutQuad(q, ErrorOp, NulSym, NulSym, r) ELSE SubQuad(q) END END END END FoldCaseBounds ; (* CodeCaseBounds - attempts to resolve whether the case bounds are legal. This should resolve at compile time as all case bounds must be constants. We introduce a CodeCaseBounds as it might be possible that constants have just been declared during the code generation of this function. *) PROCEDURE CodeCaseBounds (tokenno: CARDINAL; caseList: CARDINAL) ; BEGIN IF CaseBoundsResolved (tokenno, caseList) THEN IF TypeCaseBounds (caseList) THEN (* nothing to do *) END ; IF OverlappingCaseBounds (caseList) THEN (* nothing to do *) END ; IF MissingCaseBounds (tokenno, caseList) THEN (* nothing to do *) END ELSE MetaErrorT0 (tokenno, '{%E}the CASE statement ranges must be constants') END END CodeCaseBounds ; (* MakeAndDeclareConstLit - creates a constant of value and declares it to GCC. *) PROCEDURE MakeAndDeclareConstLit (tokenno: CARDINAL; value: Name; type: CARDINAL) : CARDINAL ; VAR constant: CARDINAL ; BEGIN constant := MakeConstLit (tokenno, value, type) ; TryDeclareConstant (tokenno, constant) ; (* use quad tokenno, rather than the range tokenNo *) Assert (GccKnowsAbout (constant)) ; RETURN constant END MakeAndDeclareConstLit ; (* FoldNonPosDiv - attempts to fold the bound checking for a divide expression. *) PROCEDURE FoldNonPosDiv (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p : Range ; zero: CARDINAL ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) IF GccKnowsAbout(expr) AND IsConst(expr) THEN zero := MakeAndDeclareConstLit (tokenno, MakeKey ('0'), ZType) ; IF IsGreaterOrEqualConversion (TokenToLocation (tokenno), zero, des, expr) THEN MetaErrorT2 (tokenNo, 'the divisor {%2Wa} in this division expression is less than or equal to zero, this will cause an exception to be raised before the result is assigned to the designator {%1a}', des, expr) ; PutQuad (q, ErrorOp, NulSym, NulSym, r) END END END END FoldNonPosDiv ; (* FoldNonPosMod - attempts to fold the bound checking for a modulus expression. *) PROCEDURE FoldNonPosMod (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p : Range ; zero: CARDINAL ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) IF GccKnowsAbout(expr) AND IsConst(expr) THEN zero := MakeAndDeclareConstLit (tokenno, MakeKey('0'), ZType) ; IF IsGreaterOrEqualConversion (TokenToLocation(tokenno), zero, des, expr) THEN MetaErrorT2 (tokenNo, 'the divisor {%2Wa} in this modulus expression is less than or equal to zero, this will cause an exception to be raised before the result is assigned to the designator {%1a}', des, expr) ; PutQuad (q, ErrorOp, NulSym, NulSym, r) END END END END FoldNonPosMod ; (* FoldZeroDiv - *) PROCEDURE FoldZeroDiv (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p : Range ; zero: CARDINAL ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) IF GccKnowsAbout(expr) AND IsConst(expr) THEN zero := MakeAndDeclareConstLit (tokenno, MakeKey ('0'), ZType) ; IF IsEqualConversion (zero, des, expr) THEN MetaErrorT2 (tokenNo, 'the divisor {%2Wa} in this division expression is equal to zero, this will cause an exception to be raised before the result is assigned to the designator {%1a}', des, expr) ; PutQuad (q, ErrorOp, NulSym, NulSym, r) END END END END FoldZeroDiv ; (* FoldZeroRem - *) PROCEDURE FoldZeroRem (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p : Range ; zero: CARDINAL ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) IF GccKnowsAbout(expr) AND IsConst(expr) THEN zero := MakeAndDeclareConstLit (tokenno, MakeKey ('0'), ZType) ; IF IsEqualConversion (zero, des, expr) THEN MetaErrorT2 (tokenNo, 'the divisor {%2Wa} in this remainder expression is equal to zero, this will cause an exception to be raised before the result is assigned to the designator {%1a}', des, expr) ; PutQuad (q, ErrorOp, NulSym, NulSym, r) END END END END FoldZeroRem ; (* FoldRangeCheck - attempts to resolve the range check, r. If it evaluates to true then it is replaced by an ErrorOp elsif it evaluates to false then it is removed else it is left alone *) PROCEDURE FoldRangeCheck (tokenno: CARDINAL; q: CARDINAL; r: CARDINAL) ; VAR p: Range ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO CASE type OF assignment : FoldAssignment(tokenno, q, r) | returnassignment : FoldReturn(tokenno, q, r) | (* subrangeassignment : | unused currently *) inc : FoldInc(tokenno, q, r) | dec : FoldDec(tokenno, q, r) | incl : FoldIncl(tokenno, q, r) | excl : FoldExcl(tokenno, q, r) | shift : FoldShift(tokenno, q, r) | rotate : FoldRotate(tokenno, q, r) | typeassign : FoldTypeCheck(tokenno, q, r) | typeparam : FoldTypeCheck(tokenno, q, r) | typeexpr : FoldTypeCheck(tokenno, q, r) | paramassign : FoldParameterAssign(tokenno, q, r) | staticarraysubscript : FoldStaticArraySubscript(tokenno, q, r) | dynamicarraysubscript: FoldDynamicArraySubscript(tokenno, q, r) | forloopbegin : FoldForLoopBegin(tokenno, q, r) | forloopto : FoldForLoopTo(tokenno, q, r) | forloopend : RETURN (* unable to fold anything at this point, des, will be variable *) | pointernil : FoldNil(tokenno, q, r) | noreturn : RETURN (* nothing to fold *) | noelse : RETURN (* nothing to fold *) | casebounds : FoldCaseBounds(tokenno, q, r) | wholenonposdiv : FoldNonPosDiv(tokenno, q, r) | wholenonposmod : FoldNonPosMod(tokenno, q, r) | wholezerodiv : FoldZeroDiv(tokenno, q, r) | wholezerorem : FoldZeroRem(tokenno, q, r) | none : SubQuad(q) ELSE InternalError ('unexpected case') END END END FoldRangeCheck ; (* DeReferenceLValue - returns a Tree which is either ModGcc(expr) or Mod2Gcc ( *expr) depending whether, expr, is an LValue. *) PROCEDURE DeReferenceLValue (tokenno: CARDINAL; expr: CARDINAL) : Tree ; VAR e : Tree ; location: location_t ; BEGIN location := TokenToLocation(tokenno) ; e := Mod2Gcc(expr) ; IF GetMode(expr)=LeftValue THEN e := BuildIndirect(location, e, Mod2Gcc(GetType(expr))) END ; RETURN( e ) END DeReferenceLValue ; (* BuildStringParam - builds a C style string parameter which will be passed as an ADDRESS type. *) PROCEDURE BuildStringParam (tokenno: CARDINAL; s: String) ; BEGIN BuildStringParamLoc (TokenToLocation(tokenno), s) END BuildStringParam ; (* BuildStringParamLoc - builds a C style string parameter which will be passed as an ADDRESS type. *) PROCEDURE BuildStringParamLoc (location: location_t; s: String) ; BEGIN BuildParam (location, BuildConvert (location, Mod2Gcc (Address), BuildAddr (location, BuildStringConstant (string(s), Length(s)), FALSE), FALSE)) END BuildStringParamLoc ; (* CodeErrorCheck - returns a Tree calling the approprate exception handler. *) PROCEDURE CodeErrorCheck (r: CARDINAL; function, message: String) : Tree ; VAR filename: String ; line, column : CARDINAL ; p : Range ; f : Tree ; location: location_t ; BEGIN IF HandlerExists (r) THEN IF message = NIL THEN message := GetRangeErrorMessage (r) END ; message := FillInParameters (r, message) ; p := GetIndice (RangeIndex, r) ; WITH p^ DO filename := FindFileNameFromToken (tokenNo, 0) ; line := TokenToLineNo (tokenNo, 0) ; column := TokenToColumnNo (tokenNo, 0) ; location := TokenToLocation (tokenNo) ; f := Mod2Gcc (lookupExceptionHandler (type)) ; BuildStringParam (tokenNo, message) ; BuildStringParam (tokenNo, function) ; BuildParam (location, BuildIntegerConstant (column)) ; BuildParam (location, BuildIntegerConstant (line)) ; BuildStringParam (tokenNo, filename) ; RETURN BuildProcedureCallTree (location, f, NIL) END ELSE RETURN NIL END END CodeErrorCheck ; (* IssueWarning - issue a warning. The compiler knows that this basic block can be reached and we are in scope, function. *) PROCEDURE IssueWarning (function: String; r: CARDINAL) ; VAR p: Range ; s: String ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO CASE type OF assignment : s := InitString('if the assignment is ever executed then the designator {%1Wa} will exceed the type range {%1ts:of {%1ts}}') | returnassignment : s := InitString('if the value {%2Wa} is returned from procedure function {%1Wa} then it will exceed the type range {%1ts:of {%1ts}}') | subrangeassignment : InternalError ('not expecting this case value') | inc : s := InitString('if the INC is ever executed the expression {%2Wa} will cause an overflow error for the designator {%1a} as it exceeds the type range {%1ts:of {%1ts}}') | dec : s := InitString('if the DEC is ever executed the expression {%2Wa} will cause an underflow error for the designator {%1a} as it exceeds the type range {%1ts:of {%1ts}}') | incl : s := InitString('the expression {%2Wa} given in the INCL exceeds the type range {%1ts} of the designator {%1a}') | excl : s := InitString('the expression {%2Wa} given in the EXCL exceeds the type range {%1ts} of the designator {%1a}') | shift : s := InitString('the expression {%2Wa} given in the second parameter to SHIFT exceeds the type range {%1ts} of the first parameter {%1a}') | rotate : s := InitString('the expression {%2Wa} given in the second parameter to ROTATE exceeds the type range {%1ts} of the first parameter {%1a}') | typeassign : s := InitString('') | typeparam : s := InitString('') | typeexpr : s := InitString('') | paramassign : s := InitString('if this call is executed then the actual parameter {%2Wa} will be out of range of the {%3N} formal parameter {%1a}') | staticarraysubscript : s := InitString('if this access to the static array {%1Wa:{%2a:{%1a}[{%2a}]}} is ever made then the index will be out of bounds in the {%3N} array subscript') | dynamicarraysubscript: s := InitString('if this access to the dynamic array {%1Wa:{%2a:{%1a}[{%2a}]}} is ever made then the index will be out of bounds in the {%3N} array subscript') | forloopbegin : s := InitString('if the assignment in this FOR loop is ever executed then the designator {%1Wa} will be exceed the type range {%1ts:of {%1ts}}') | forloopto : s := InitString('the final value {%2Wa} in this FOR loop will be out of bounds {%1ts:of type {%1ts}} if ever executed') | forloopend : s := InitString('the FOR loop will cause the designator {%1Wa} to be out of bounds when the BY value {%2a} is added') | pointernil : s := InitString('if this pointer value {%1Wa} is ever dereferenced it will cause an exception') | noreturn : s := InitString('{%1W:}this function will exit without executing a RETURN statement') | noelse : s := InitString('{%1W:}this CASE statement does not have an ELSE statement') | casebounds : s := InitString('{%1W:}this CASE statement has overlapping ranges') | wholenonposdiv : s := InitString('this division expression {%2Wa} will cause an exception as this divisor is less than or equal to zero') | wholenonposmod : s := InitString('this modulus expression {%2Wa} will cause an exception as this divisor is less than or equal to zero') | wholezerodiv : s := InitString('this division expression {%2Wa} will cause an exception as the divisor is zero') | wholezerorem : s := InitString('this remainder expression {%2Wa} will cause an exception as the divisor is zero') | none : InternalError ('unexpected value') ELSE InternalError ('enumeration value unknown') END ; s := ConCat (s, Mark (InitString (' in ('))) ; s := ConCat (s, function) ; s := ConCatChar (s, ')') ; MetaErrorStringT3 (tokenNo, s, des, expr, dimension) ; (* FlushErrors *) END END IssueWarning ; (* CodeErrorCheckLoc - generate a runtime error message positioned at location and in function. If function is NIL then the error scope is used. *) PROCEDURE CodeErrorCheckLoc (location: location_t; function, message: ADDRESS; func: CARDINAL) : Tree ; VAR scope, errorMessage: String ; t : Tree ; filename : String ; line, column : CARDINAL ; BEGIN IF func = NulSym THEN RETURN NIL ELSE t := Mod2Gcc (func) ; IF t # NIL THEN filename := InitStringCharStar (GetFilenameFromLocation (location)) ; Assert (message # NIL) ; errorMessage := InitStringCharStar (message) ; column := GetColumnNoFromLocation (location) ; line := GetLineNoFromLocation (location) ; BuildStringParamLoc (location, errorMessage) ; IF function = NIL THEN scope := GetAnnounceScope (filename, NIL) ELSE scope := quoteOpen (InitString ('')) ; scope := ConCat (scope, Mark (InitStringCharStar (function))) ; scope := ConCat (InitString ("procedure "), quoteClose (scope)) END ; BuildStringParamLoc (location, scope) ; BuildParam (location, BuildIntegerConstant (column)) ; BuildParam (location, BuildIntegerConstant (line)) ; BuildStringParamLoc (location, filename) ; t := BuildProcedureCallTree (location, t, NIL) ; (* filename := KillString (filename) ; scope := KillString (scope) ; errorMessage := KillString (errorMessage) *) END ; RETURN t END END CodeErrorCheckLoc ; (* IssueWarningLoc - *) PROCEDURE IssueWarningLoc (location: location_t; message: ADDRESS) ; VAR s: String ; BEGIN s := InitString ("numerical overflow detected when performing ") ; s := ConCat (s, Mark (InitStringCharStar (message))) ; ErrorAt (location, string (s)) ; s := KillString (s) END IssueWarningLoc ; (* BuildIfCallWholeHandlerLoc - return a Tree containing a runtime test whether, condition, is true. *) PROCEDURE BuildIfCallWholeHandlerLoc (location: location_t; condition: Tree; scope, message: ADDRESS) : Tree ; BEGIN RETURN BuildIfCallHandlerLoc (location, condition, scope, message, ExceptionWholeValue) END BuildIfCallWholeHandlerLoc ; (* BuildIfCallRealHandlerLoc - return a Tree containing a runtime test whether, condition, is true. *) PROCEDURE BuildIfCallRealHandlerLoc (location: location_t; condition: Tree; scope, message: ADDRESS) : Tree ; BEGIN RETURN BuildIfCallHandlerLoc (location, condition, scope, message, ExceptionRealValue) END BuildIfCallRealHandlerLoc ; (* BuildIfCallHandlerLoc - return a Tree containing a runtime test whether, condition, is true. *) PROCEDURE BuildIfCallHandlerLoc (location: location_t; condition: Tree; scope, message: ADDRESS; func: CARDINAL) : Tree ; BEGIN IF IsTrue (condition) THEN IssueWarningLoc (location, message) END ; RETURN BuildIfThenDoEnd (condition, CodeErrorCheckLoc (location, scope, message, func)) END BuildIfCallHandlerLoc ; (* BuildIfCallHandler - *) PROCEDURE BuildIfCallHandler (condition: Tree; r: CARDINAL; function, message: String; warning: BOOLEAN) : Tree ; BEGIN IF warning AND IsTrue (condition) THEN IssueWarning (function, r) END ; RETURN BuildIfThenDoEnd (condition, CodeErrorCheck (r, function, message)) END BuildIfCallHandler ; (* RangeCheckReal - *) PROCEDURE RangeCheckReal (p: Range; r: CARDINAL; function, message: String) ; VAR e, condition: Tree ; location : location_t ; BEGIN WITH p^ DO location := TokenToLocation (tokenNo) ; e := DeReferenceLValue (tokenNo, expr) ; condition := BuildEqualTo (location, BuiltInIsfinite (location, e), GetIntegerZero (location)) ; AddStatement (location, BuildIfCallHandler (condition, r, function, message, TRUE)) ; END END RangeCheckReal ; (* RangeCheckOrdinal - *) PROCEDURE RangeCheckOrdinal (p: Range; r: CARDINAL; function, message: String) ; VAR condition, desMin, desMax, exprMin, exprMax: Tree ; location : location_t ; BEGIN WITH p^ DO location := TokenToLocation(tokenNo) ; IF GetMinMax(tokenNo, exprLowestType, exprMin, exprMax) AND GetMinMax(tokenNo, desLowestType, desMin, desMax) THEN IF OverlapsRange(desMin, desMax, exprMin, exprMax) THEN IF IsGreater(desMin, exprMin) THEN condition := BuildLessThan(location, DeReferenceLValue(tokenNo, expr), BuildConvert(location, Mod2Gcc(exprLowestType), desMin, FALSE)) ; AddStatement(location, BuildIfCallHandler(condition, r, function, message, TRUE)) END ; IF IsGreater(exprMax, desMax) THEN condition := BuildGreaterThan(location, DeReferenceLValue(tokenNo, expr), BuildConvert(location, Mod2Gcc(exprLowestType), desMax, FALSE)) ; AddStatement(location, BuildIfCallHandler(condition, r, function, message, TRUE)) END ELSE MetaErrorStringT3 (tokenNo, message, des, expr, paramNo) END END END END RangeCheckOrdinal ; (* DoCodeAssignmentExprType - *) PROCEDURE DoCodeAssignmentExprType (p: Range; r: CARDINAL; function, message: String) ; BEGIN WITH p^ DO IF GccKnowsAbout(desLowestType) AND GccKnowsAbout(exprLowestType) THEN IF IsRealType(desLowestType) AND IsRealType(exprLowestType) THEN RangeCheckReal (p, r, function, message) ELSE RangeCheckOrdinal (p, r, function, message) END ELSE InternalError ('should have resolved these types') END END END DoCodeAssignmentExprType ; (* DoCodeAssignmentWithoutExprType - *) PROCEDURE DoCodeAssignmentWithoutExprType (p: Range; r: CARDINAL; function, message: String) ; VAR condition, desMin, desMax: Tree ; location : location_t ; BEGIN WITH p^ DO location := TokenToLocation(tokenNo) ; IF GccKnowsAbout(desLowestType) THEN IF GetMinMax(tokenNo, desLowestType, desMin, desMax) THEN condition := BuildLessThan(location, BuildConvert(location, Mod2Gcc(desLowestType), DeReferenceLValue(tokenNo, expr), FALSE), desMin) ; AddStatement(location, BuildIfCallHandler(condition, r, function, message, TRUE)) ; condition := BuildGreaterThan(location, BuildConvert(location, Mod2Gcc(desLowestType), DeReferenceLValue(tokenNo, expr), FALSE), desMax) ; AddStatement(location, BuildIfCallHandler(condition, r, function, message, TRUE)) END ELSE InternalError ('should have resolved this type') END END END DoCodeAssignmentWithoutExprType ; (* DoCodeAssignment - *) PROCEDURE DoCodeAssignment (tokenno: CARDINAL; r: CARDINAL; function, message: String) ; VAR p: Range ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenNo, des) ; TryDeclareConstant(tokenNo, expr) ; DeclareConstructor(tokenno, 0, expr) ; IF desLowestType#NulSym THEN Assert(GccKnowsAbout(expr)) ; IF exprLowestType=NulSym THEN DoCodeAssignmentWithoutExprType (p, r, function, message) ELSE DoCodeAssignmentExprType (p, r, function, message) END END END END DoCodeAssignment ; (* CodeAssignment - *) PROCEDURE CodeAssignment (tokenno: CARDINAL; r: CARDINAL; function, message: String) ; BEGIN DoCodeAssignment (tokenno, r, function, message) END CodeAssignment ; (* CodeParameterAssign - *) PROCEDURE CodeParameterAssign (tokenno: CARDINAL; r: CARDINAL; function, message: String) ; BEGIN DoCodeAssignment (tokenno, r, function, message) END CodeParameterAssign ; (* CodeReturn - *) PROCEDURE CodeReturn (tokenno: CARDINAL; r: CARDINAL; function, message: String) ; BEGIN DoCodeAssignment (tokenno, r, function, message) END CodeReturn ; (* IfOutsideLimitsDo - *) PROCEDURE IfOutsideLimitsDo (tokenno: CARDINAL; min, expr, max: Tree; r: CARDINAL; function, message: String) ; VAR condition: Tree ; location : location_t ; BEGIN location := TokenToLocation (tokenno) ; condition := BuildGreaterThan (location, min, expr) ; AddStatement (location, BuildIfThenDoEnd (condition, CodeErrorCheck (r, function, message))) ; condition := BuildLessThan (location, max, expr) ; AddStatement (location, BuildIfThenDoEnd (condition, CodeErrorCheck (r, function, message))) END IfOutsideLimitsDo ; (* CodeInc - *) PROCEDURE CodeInc (tokenno: CARDINAL; r: CARDINAL; function, message: String) ; VAR p : Range ; t, condition, e, desMin, desMax: Tree ; location : location_t ; BEGIN location := TokenToLocation(tokenno) ; p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenNo, des) ; TryDeclareConstant(tokenNo, expr) ; IF desLowestType#NulSym THEN IF GccKnowsAbout(expr) AND GccKnowsAbout(desLowestType) THEN IF GetMinMax(tokenno, desLowestType, desMin, desMax) THEN e := BuildConvert(location, GetTreeType(desMin), DeReferenceLValue(tokenno, expr), FALSE) ; IfOutsideLimitsDo(tokenNo, BuildConvert(location, GetTreeType(desMin), GetIntegerZero(location), FALSE), e, desMax, r, function, message) ; t := BuildSub(location, desMax, BuildConvert(location, Mod2Gcc(desLowestType), e, FALSE), FALSE) ; condition := BuildGreaterThan(location, Mod2Gcc(des), t) ; AddStatement (location, BuildIfThenDoEnd (condition, CodeErrorCheck (r, function, message))) END ELSE InternalError ('should have resolved these types') END END END END CodeInc ; (* CodeDec - *) PROCEDURE CodeDec (tokenno: CARDINAL; r: CARDINAL; function, message: String) ; VAR p : Range ; t, condition, e, desMin, desMax: Tree ; location : location_t ; BEGIN location := TokenToLocation(tokenno) ; p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenNo, des) ; TryDeclareConstant(tokenNo, expr) ; IF desLowestType#NulSym THEN IF GccKnowsAbout(expr) AND GccKnowsAbout(desLowestType) THEN IF GetMinMax(tokenno, desLowestType, desMin, desMax) THEN e := BuildConvert(location, GetTreeType(desMin), DeReferenceLValue(tokenno, expr), FALSE) ; IfOutsideLimitsDo(tokenNo, BuildConvert(location, GetTreeType(desMin), GetIntegerZero(location), FALSE), e, desMax, r, function, message) ; t := BuildSub(location, BuildConvert(location, Mod2Gcc(desLowestType), e, FALSE), desMin, FALSE) ; condition := BuildLessThan(location, Mod2Gcc(des), t) ; AddStatement (location, BuildIfThenDoEnd (condition, CodeErrorCheck (r, function, message))) END ELSE InternalError ('should have resolved these types') END END END END CodeDec ; (* CodeInclExcl - *) PROCEDURE CodeInclExcl (tokenno: CARDINAL; r: CARDINAL; function, message: String) ; VAR p : Range ; e, desMin, desMax: Tree ; location : location_t ; BEGIN location := TokenToLocation(tokenno) ; p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenNo, des) ; TryDeclareConstant(tokenNo, expr) ; desLowestType := SkipType(GetType(des)) ; IF desLowestType#NulSym THEN IF GccKnowsAbout(expr) AND GccKnowsAbout(desLowestType) THEN IF GetMinMax(tokenno, desLowestType, desMin, desMax) THEN e := BuildConvert(location, GetTreeType(desMin), DeReferenceLValue(tokenno, expr), FALSE) ; IfOutsideLimitsDo(tokenNo, desMin, e, desMax, r, function, message) (* this should not be used for incl/excl as des is a set type t := BuildSub(location, desMax, BuildConvert(location, Mod2Gcc(desLowestType), e, FALSE), FALSE) ; condition := BuildGreaterThan(Mod2Gcc(des), t) ; AddStatement(location, BuildIfThenDoEnd(condition, CodeErrorCheck(r, function, message))) *) END ELSE InternalError ('should have resolved these types') END END END END CodeInclExcl ; (* CodeShiftRotate - ensure that the bit shift is within the range -(MAX(set)-MIN(set)+1)..(MAX(set)-MIN(set)+1) *) PROCEDURE CodeShiftRotate (tokenno: CARDINAL; r: CARDINAL; function, message: String) ; VAR ofType : CARDINAL ; p : Range ; e, shiftMin, shiftMax, desMin, desMax : Tree ; location : location_t ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenNo, des) ; TryDeclareConstant(tokenNo, expr) ; desLowestType := SkipType(GetType(des)) ; IF desLowestType#NulSym THEN ofType := SkipType(GetType(desLowestType)) ; IF GccKnowsAbout(expr) AND GccKnowsAbout(ofType) THEN IF GetMinMax(tokenno, ofType, desMin, desMax) THEN location := TokenToLocation(tokenNo) ; desMin := BuildConvert(location, GetIntegerType(), desMin, FALSE) ; desMax := BuildConvert(location, GetIntegerType(), desMax, FALSE) ; shiftMax := BuildAdd(location, BuildSub(location, desMax, desMin, FALSE), GetIntegerOne(location), FALSE) ; shiftMin := BuildNegate(location, shiftMax, FALSE) ; e := BuildConvert(location, GetIntegerType(), DeReferenceLValue(tokenno, expr), FALSE) ; IfOutsideLimitsDo(tokenNo, shiftMin, e, shiftMax, r, function, message) END ELSE InternalError ('should have resolved these types') END END END END CodeShiftRotate ; (* CodeStaticArraySubscript - *) PROCEDURE CodeStaticArraySubscript (tokenno: CARDINAL; r: CARDINAL; function, message: String) ; VAR p : Range ; desMin, desMax: Tree ; location : location_t ; BEGIN location := TokenToLocation (tokenno) ; p := GetIndice (RangeIndex, r) ; WITH p^ DO TryDeclareConstant (tokenNo, expr) ; IF GccKnowsAbout (expr) AND GccKnowsAbout (desLowestType) THEN IF GetMinMax (tokenno, desLowestType, desMin, desMax) THEN IfOutsideLimitsDo (tokenno, desMin, BuildConvert (location, GetTreeType (desMin), DeReferenceLValue (tokenno, expr), FALSE), desMax, r, function, message) ELSE InternalError ('should have resolved the bounds of the static array') END ELSE InternalError ('should have resolved these types') END END END CodeStaticArraySubscript ; (* CodeDynamicArraySubscript - *) PROCEDURE CodeDynamicArraySubscript (tokenno: CARDINAL; r: CARDINAL; function, message: String) ; VAR UnboundedType: CARDINAL ; p : Range ; high, e : Tree ; location : location_t ; BEGIN location := TokenToLocation(tokenno) ; p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenNo, expr) ; Assert(IsVar(des)) ; IF GccKnowsAbout(expr) AND GccKnowsAbout(des) THEN UnboundedType := GetType(des) ; Assert(IsUnbounded(UnboundedType)) ; high := BuildConvert(location, GetIntegerType(), GetHighFromUnbounded(location, dimension, des), FALSE) ; e := BuildConvert(location, GetIntegerType(), DeReferenceLValue(tokenno, expr), FALSE) ; IfOutsideLimitsDo(tokenNo, GetIntegerZero(location), e, high, r, function, message) ELSE InternalError ('should have resolved these types') END END END CodeDynamicArraySubscript ; (* CodeForLoopBegin - *) PROCEDURE CodeForLoopBegin (tokenno: CARDINAL; r: CARDINAL; function, message: String) ; BEGIN DoCodeAssignment(tokenno, r, function, message) END CodeForLoopBegin ; (* CodeForLoopTo - *) PROCEDURE CodeForLoopTo (tokenno: CARDINAL; r: CARDINAL; function, message: String) ; BEGIN DoCodeAssignment(tokenno, r, function, message) END CodeForLoopTo ; (* Pseudo template code for CodeLoopEnd: PROCEDURE CheckCardinalInteger (des: CARDINAL; inc: INTEGER) ; VAR room, lg : CARDINAL ; BEGIN IF inc>=0 THEN IF des>=0 THEN lg := VAL(CARDINAL, inc) ; room := MAX(CARDINAL)-des ; IF lg>room THEN printf("increment exceeds range at end of FOR loop\n") ; exit (2) END ELSE (* inc can never cause an overflow given its type *) END ELSE (* inc < 0 *) IF des>VAL(CARDINAL, MAX(INTEGER)) THEN (* inc can never cause an underflow given its range *) ELSE (* des <= MAX(INTEGER) *) IF des=MIN(INTEGER) THEN printf("increment exceeds range at end of FOR loop\n") ; exit (4) ELSE IF inc=MIN(INTEGER) THEN IF des=0 THEN printf("increment exceeds range at end of FOR loop\n") ; exit (5) END ELSE lg := VAL(CARDINAL, -inc) ; IF lg>des THEN printf("increment exceeds range at end of FOR loop\n") ; exit (5) END END END END END END CheckCardinalInteger ; PROCEDURE CheckCardinalCardinal (des: CARDINAL; inc: CARDINAL) ; BEGIN IF MAX(CARDINAL)-des= 0) [c1] *) c2 := BuildGreaterThanOrEqual(location, Mod2Gcc(des), dz) ; (* if (des >= 0) [c2] *) lg1 := BuildConvert(location, Mod2Gcc(desLowestType), inc, FALSE) ; room := BuildSub(location, dmax, Mod2Gcc(des), FALSE) ; c3 := BuildGreaterThan(location, lg1, room) ; (* [c3] *) (* WarnIf(IsTrue(c1) AND IsTrue(c2) AND IsTrue(c3), function, message) ; --implement me-- *) s3 := BuildIfCallHandler(c3, r, function, message, FALSE) ; s2 := BuildIfThenDoEnd(c2, s3) ; (* else *) (* (* inc < 0 *) [s4] *) (* if (des <= val(desLowestType, emax) [c4] *) c4 := BuildLessThanOrEqual(location, Mod2Gcc(des), BuildConvert(location, Mod2Gcc(desLowestType), emax, FALSE)) ; (* (* des <= MAX(exprLowestType) *) *) desoftypee := BuildConvert(location, Mod2Gcc(exprLowestType), Mod2Gcc(des), FALSE) ; c5 := BuildEqualTo(location, desoftypee, emin) ; (* [c5] *) s5 := BuildIfCallHandler(c5, r, function, message, FALSE) ; (* if des = emin *) (* error [s5] *) (* end *) c6 := BuildEqualTo(location, inc, emin) ; (* [c6] *) (* if inc = emin *) (* if des = 0 [c7] *) c7 := BuildEqualTo(location, Mod2Gcc(des), dz) ; s7 := BuildIfCallHandler(c7, r, function, message, FALSE) ; (* end *) (* else *) (* lg2 = VAL(desLowestType, -inc) [s8] *) lg2 := BuildConvert(location, Mod2Gcc(desLowestType), BuildNegate(location, inc, FALSE), FALSE) ; (* if lg2 > des *) (* error *) c8 := BuildGreaterThan(location, lg2, Mod2Gcc(des)) ; s8 := BuildIfCallHandler(c8, r, function, message, FALSE) ; (* end *) (* end *) (* end *) (* end *) (* end *) END ; s6 := BuildIfThenElseEnd(c6, s7, s8) ; s4 := BuildIfThenElseEnd(c4, s5, s6) ; s1 := BuildIfThenElseEnd(c1, s2, s4) ; AddStatement(location, s1) END DiffTypesCodeForLoopEnd ; (* CodeForLoopEnd - checks to see that des := des + expr does not overflow. This is called at the end of the for loop. It is more complex than it initially seems as des and expr might be different types. *) PROCEDURE CodeForLoopEnd (tokenno: CARDINAL; r: CARDINAL; function, message: String) ; VAR isCard : BOOLEAN ; p : Range ; dmin, dmax, emin, emax: Tree ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenno, des) ; (* use quad tokenno, rather than the range tokenNo *) TryDeclareConstant(tokenno, expr) ; (* use quad tokenno, rather than the range tokenNo *) IF desLowestType#NulSym THEN Assert(GccKnowsAbout(expr)) ; IF GccKnowsAbout(desLowestType) AND GetMinMax(tokenno, desLowestType, dmin, dmax) AND GccKnowsAbout(exprLowestType) AND GetMinMax(tokenno, exprLowestType, emin, emax) THEN PushIntegerTree(dmin) ; PushInt(0) ; isCard := GreEqu(tokenno) ; IF (desLowestType=exprLowestType) AND isCard THEN SameTypesCodeForLoopEnd(tokenno, r, function, message, p, dmax) ELSE DiffTypesCodeForLoopEnd(tokenno, r, function, message, p, dmax, emin, emax) END END END END END CodeForLoopEnd ; (* CodeNil - *) PROCEDURE CodeNil (r: CARDINAL; function, message: String) ; VAR p : Range ; condition, t: Tree ; location : location_t ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenNo, des) ; (* IF GetMode(des)=LeftValue THEN (* t := BuildIndirect(Mod2Gcc(des), Mod2Gcc(GetType(des))) *) ELSE t := Mod2Gcc(des) END ; *) t := Mod2Gcc(des) ; location := TokenToLocation(tokenNo) ; condition := BuildEqualTo(location, BuildConvert(location, GetPointerType(), t, FALSE), GetPointerZero(location)) ; AddStatement(location, BuildIfCallHandler(condition, r, function, message, TRUE)) END END CodeNil ; (* CodeWholeNonPos - generates range check code for expr<=0. *) PROCEDURE CodeWholeNonPos (tokenno: CARDINAL; r: CARDINAL; function, message: String) ; VAR zero : CARDINAL ; p : Range ; condition, e : Tree ; location : location_t ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant (tokenNo, expr) ; IF GccKnowsAbout (expr) THEN location := TokenToLocation (tokenno) ; e := ZConstToTypedConst (LValueToGenericPtr(location, expr), expr, des) ; zero := MakeAndDeclareConstLit (tokenno, MakeKey('0'), ZType) ; condition := BuildLessThanOrEqual (location, e, Mod2Gcc (zero)) ; AddStatement (location, BuildIfThenDoEnd (condition, CodeErrorCheck (r, function, message))) ELSE InternalError ('should have resolved expr') END END END CodeWholeNonPos ; (* CodeWholeZero - generates range check code for expr=0. *) PROCEDURE CodeWholeZero (tokenno: CARDINAL; r: CARDINAL; function, message: String) ; VAR zero : CARDINAL ; p : Range ; condition, e : Tree ; location : location_t ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO TryDeclareConstant(tokenNo, expr) ; IF GccKnowsAbout(expr) THEN location := TokenToLocation(tokenno) ; e := ZConstToTypedConst(LValueToGenericPtr(location, expr), expr, des) ; zero := MakeAndDeclareConstLit (tokenno, MakeKey('0'), ZType) ; condition := BuildEqualTo(location, e, BuildConvert(location, GetTreeType(e), Mod2Gcc(zero), FALSE)) ; AddStatement(location, BuildIfThenDoEnd(condition, CodeErrorCheck(r, function, message))) ELSE InternalError ('should have resolved expr') END END END CodeWholeZero ; (* InitCaseBounds - creates a case bound range check. *) PROCEDURE InitCaseBounds (b: CARDINAL) : CARDINAL ; VAR p: Range ; r: CARDINAL ; BEGIN r := InitRange() ; p := PutRangeNoEval(GetIndice(RangeIndex, r), casebounds) ; p^.caseList := b ; RETURN( r ) END InitCaseBounds ; (* FillInParameters - *) PROCEDURE FillInParameters (r: CARDINAL; s: String) : String ; VAR p: Range ; BEGIN p := GetIndice (RangeIndex, r) ; WITH p^ DO CASE type OF assignment : s := MetaString3 (s, des, expr, dimension) | returnassignment : s := MetaString3 (s, des, expr, dimension) | subrangeassignment : InternalError ('unexpected case') | inc : s := MetaString3 (s, des, expr, dimension) | dec : s := MetaString3 (s, des, expr, dimension) | incl : s := MetaString3 (s, des, expr, dimension) | excl : s := MetaString3 (s, des, expr, dimension) | shift : s := MetaString3 (s, des, expr, dimension) | rotate : s := MetaString3 (s, des, expr, dimension) | typeassign : | typeparam : | typeexpr : | paramassign : s := MetaString3 (s, des, expr, paramNo) | staticarraysubscript : s := MetaString3 (s, des, expr, dimension) | dynamicarraysubscript: s := MetaString3 (s, des, expr, dimension) | forloopbegin : s := MetaString3 (s, des, expr, dimension) | forloopto : s := MetaString3 (s, des, expr, dimension) | forloopend : s := MetaString3 (s, des, expr, dimension) | pointernil : s := MetaString3 (s, des, expr, dimension) | noreturn : s := MetaString3 (s, des, expr, dimension) | noelse : s := MetaString3 (s, des, expr, dimension) | casebounds : s := MetaString3 (s, des, expr, dimension) | wholenonposdiv : s := MetaString3 (s, des, expr, dimension) | wholenonposmod : s := MetaString3 (s, des, expr, dimension) | wholezerodiv : s := MetaString3 (s, des, expr, dimension) | wholezerorem : s := MetaString3 (s, des, expr, dimension) | none : | ELSE InternalError ('unexpected case') END END ; RETURN s END FillInParameters ; (* GetRangeErrorMessage - returns a specific error message for the range, r. It assumes the 3 parameters to be supplied on the MetaError parameter list are: dest, expr, paramNo or dimension. XYZ 'the initial assignment to {%1a} at the start of the FOR loop will cause a range error, as the type range of {%1taD} does not overlap with {%2tad}') 'the final TO value {%2a} of the FOR loop will cause a range error with the iterator variable {%1a}') *) PROCEDURE GetRangeErrorMessage (r: CARDINAL) : String ; VAR p: Range ; s: String ; BEGIN p := GetIndice (RangeIndex, r) ; WITH p^ DO CASE type OF assignment : s := InitString ('assignment will cause a range error, as the runtime instance value of {%1tad} does not overlap with the type {%2tad}') | returnassignment : s := InitString ('attempting to return {%2Wa} from a procedure function {%1a} which will exceed exceed the range of type {%1tad}') | subrangeassignment : InternalError ('unexpected case') | inc : s := InitString ('if the INC is ever executed the expression {%2Wa} will cause an overflow error for the designator {%1a} as it exceeds the type range {%1ts:of {%1ts}}') | dec : s := InitString ('if the DEC is ever executed the expression {%2Wa} will cause an underflow error for the designator {%1a} as it exceeds the type range {%1ts:of {%1ts}}') | incl : s := InitString ('the expression {%2Wa} given in the INCL exceeds the type range {%1ts} of the designator {%1a}') | excl : s := InitString ('the expression {%2Wa} given in the EXCL exceeds the type range {%1ts} of the designator {%1a}') | shift : s := InitString ('the expression {%2Wa} given in the second parameter to SHIFT exceeds the type range {%1ts} of the first parameter {%1a}') | rotate : s := InitString ('the expression {%2Wa} given in the second parameter to ROTATE exceeds the type range {%1ts} of the first parameter {%1a}') | typeassign : s := NIL | typeparam : s := NIL | typeexpr : s := NIL | paramassign : s := InitString('if this call is executed then the actual parameter {%2Wa} will be out of range of the {%3N} formal parameter {%1a}') | staticarraysubscript : s := InitString('if this access to the static array {%1Wa:{%2a:{%1a}[{%2a}]}} is ever made then the index will be out of bounds in the {%3N} array subscript') | dynamicarraysubscript: s := InitString('if this access to the dynamic array {%1Wa:{%2a:{%1a}[{%2a}]}} is ever made then the index will be out of bounds in the {%3N} array subscript') | forloopbegin : s := InitString('if the assignment in this FOR loop is ever executed then the designator {%1Wa} will be exceed the type range {%1ts:of {%1ts}}') | forloopto : s := InitString('the final value {%2Wa} in this FOR loop will be out of bounds {%1ts:of type {%1ts}} if ever executed') | forloopend : s := InitString('the FOR loop will cause the designator {%1Wa} to be out of bounds when the BY value {%2a} is added') | pointernil : s := InitString('if this pointer value {%1Wa} is ever dereferenced it will cause an exception') | noreturn : s := InitString('{%1W:}this function will exit without executing a RETURN statement') | noelse : s := InitString('{%1W:}this CASE statement does not have an ELSE statement') | casebounds : s := InitString('{%1W:}this CASE statement has overlapping ranges') | wholenonposdiv : s := InitString('this division expression {%2Wa} will cause an exception as this divisor is less than or equal to zero') | wholenonposmod : s := InitString('this modulus expression {%2Wa} will cause an exception as this divisor is less than or equal to zero') | wholezerodiv : s := InitString('this division expression {%2Wa} will cause an exception as the divisor is zero') | wholezerorem : s := InitString('this remainder expression {%2Wa} will cause an exception as the divisor is zero') | none : s := NIL ELSE InternalError ('unexpected case') END END ; RETURN s END GetRangeErrorMessage ; (* CodeRangeCheck - returns a Tree representing the code for a range test defined by, r. *) PROCEDURE CodeRangeCheck (r: CARDINAL; function: String) ; VAR p : Range ; message: String ; BEGIN p := GetIndice (RangeIndex, r) ; message := GetRangeErrorMessage (r) ; WITH p^ DO CASE type OF assignment : CodeAssignment (tokenNo, r, function, message) | returnassignment : CodeReturn (tokenNo, r, function, message) | subrangeassignment : InternalError ('unexpected case') | inc : CodeInc (tokenNo, r, function, message) | dec : CodeDec (tokenNo, r, function, message) | incl, excl : CodeInclExcl (tokenNo, r, function, message) | shift, rotate : CodeShiftRotate (tokenNo, r, function, message) | typeassign : CodeTypeCheck (tokenNo, r) | typeparam : CodeTypeCheck (tokenNo, r) | typeexpr : CodeTypeCheck (tokenNo, r) | staticarraysubscript : CodeStaticArraySubscript (tokenNo, r, function, message) | dynamicarraysubscript: CodeDynamicArraySubscript (tokenNo, r, function, message) | forloopbegin : CodeForLoopBegin (tokenNo, r, function, message) | forloopto : CodeForLoopTo (tokenNo, r, function, message) | forloopend : CodeForLoopEnd (tokenNo, r, function, message) | pointernil : CodeNil (r, function, message) | noreturn : AddStatement (TokenToLocation (tokenNo), CodeErrorCheck (r, function, message)) | noelse : AddStatement (TokenToLocation (tokenNo), CodeErrorCheck (r, function, message)) | casebounds : CodeCaseBounds (tokenNo, caseList) | wholenonposdiv : CodeWholeNonPos (tokenNo, r, function, message) | wholenonposmod : CodeWholeNonPos (tokenNo, r, function, message) | wholezerodiv : CodeWholeZero (tokenNo, r, function, message) | wholezerorem : CodeWholeZero (tokenNo, r, function, message) | paramassign : CodeParameterAssign (tokenNo, r, function, message) | none : ELSE InternalError ('unexpected case') END END END CodeRangeCheck ; (* AddVarRead - checks to see whether symbol, Sym, is a variable or a parameter and if so it then adds this quadruple to the variable list. *) (* PROCEDURE AddVarRead (sym: CARDINAL; quadNo: CARDINAL) ; BEGIN IF (sym#NulSym) AND IsVar(sym) THEN PutReadQuad(sym, GetMode(sym), quadNo) END END AddVarRead ; *) (* SubVarRead - checks to see whether symbol, Sym, is a variable or a parameter and if so it then removes this quadruple from the variable list. *) (* PROCEDURE SubVarRead (sym: CARDINAL; quadNo: CARDINAL) ; BEGIN IF (sym#NulSym) AND IsVar(sym) THEN RemoveReadQuad(sym, GetMode(sym), quadNo) END END SubVarRead ; *) (* CheckRangeAddVariableRead - ensures that any references to reading variables used by this range check, r, at this, quadNo, are recorded in the symbol table. *) (* PROCEDURE CheckRangeAddVariableRead (r: CARDINAL; quadNo: CARDINAL) ; VAR p: Range ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO (* AddVarRead(des, quadNo) ; *) (* AddVarRead(expr, quadNo) *) END END CheckRangeAddVariableRead ; *) (* CheckRangeRemoveVariableRead - ensures that any references to reading variable at this quadNo are removed from the symbol table. *) (* PROCEDURE CheckRangeRemoveVariableRead (r: CARDINAL; quadNo: CARDINAL) ; VAR p: Range ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO (* SubVarRead(des, quadNo) ; *) (* SubVarRead(expr, quadNo) *) END END CheckRangeRemoveVariableRead ; *) (* WriteRangeCheck - displays debugging information about range, r. *) PROCEDURE WriteRangeCheck (r: CARDINAL) ; VAR p: Range ; BEGIN p := GetIndice(RangeIndex, r) ; WITH p^ DO CASE type OF assignment : WriteString('assignment (') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | returnassignment : WriteString('returnassignment (') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | subrangeassignment : WriteString('subrangeassignment(') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | inc : WriteString('inc(') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | dec : WriteString('dec(') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | incl : WriteString('incl(') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | excl : WriteString('excl(') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | shift : WriteString('shift(') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | rotate : WriteString('rotate(') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | typeexpr : WriteString('expr compatible (') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | typeassign : WriteString('assignment compatible (') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | typeparam : WriteString('parameter compatible (') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | paramassign : WriteString('parameter range (') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | staticarraysubscript : WriteString('staticarraysubscript(') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | dynamicarraysubscript: WriteString('dynamicarraysubscript(') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | forloopbegin : WriteString('forloopbegin(') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | forloopto : WriteString('forloopto(') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | forloopend : WriteString('forloopend(') ; WriteOperand(des) ; WriteString(', ') ; WriteOperand(expr) | pointernil : WriteString('pointernil(') ; WriteOperand(des) | noreturn : WriteString('noreturn(') | noelse : WriteString('noelse(') | casebounds : WriteString('casebounds(') ; WriteCase(caseList) | wholenonposdiv : WriteString('wholenonposdiv(') ; WriteOperand(expr) | wholenonposmod : WriteString('wholenonposmod(') ; WriteOperand(expr) | wholezerodiv : WriteString('wholezerodiv(') ; WriteOperand(expr) | wholezerorem : WriteString('wholezerorem(') ; WriteOperand(expr) | none : WriteString('none(') | ELSE InternalError ('unknown case') END ; Write(')') END END WriteRangeCheck ; (* Init - initializes the modules global variables. *) PROCEDURE Init ; BEGIN TopOfRange := 0 ; RangeIndex := InitIndex(1) END Init ; BEGIN Init END M2Range.