mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-12-05 08:47:41 +01:00
11274 lines
388 KiB
ObjectPascal
11274 lines
388 KiB
ObjectPascal
{
|
|
This file is part of the Free Component Library
|
|
|
|
Pascal source parser
|
|
Copyright (c) 2000-2005 by
|
|
Areca Systems GmbH / Sebastian Guenther, sg@freepascal.org
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
This program 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.
|
|
|
|
**********************************************************************
|
|
|
|
Abstract:
|
|
Resolves references by setting TPasElement.CustomData as TResolvedReference.
|
|
Creates search scopes for elements with sub identifiers by setting
|
|
TPasElement.CustomData as TPasScope: unit, program, library, interface,
|
|
implementation, procs
|
|
|
|
Works:
|
|
- built-in types as TPasUnresolvedSymbolRef: longint, int64, string, pointer, ...
|
|
- references in statements, error if not found
|
|
- interface and implementation types, vars, const
|
|
- params, local types, vars, const
|
|
- nested procedures
|
|
- nested forward procs, nested must be resolved before proc body
|
|
- program/library/implementation forward procs
|
|
- search in used units
|
|
- unitname.identifier
|
|
- alias types, 'type a=b'
|
|
- type alias type 'type a=type b'
|
|
- choose the most compatible overloaded procedure
|
|
- while..do
|
|
- repeat..until
|
|
- if..then..else
|
|
- binary operators
|
|
- case..of
|
|
- try..finally..except, on, else, raise
|
|
- for loop
|
|
- spot duplicates
|
|
- type cast base types
|
|
- char
|
|
- ord(), chr()
|
|
- record
|
|
- variants
|
|
- const param makes children const too
|
|
- class:
|
|
- forward declaration
|
|
- instance.a
|
|
- find ancestor, search in ancestors
|
|
- virtual, abstract, override
|
|
- method body
|
|
- Self
|
|
- inherited
|
|
- property
|
|
- read var, read function
|
|
- write var, write function
|
|
- stored function
|
|
- defaultexpr
|
|
- is and as operator
|
|
- nil
|
|
- constructor result type, rrfNewInstance
|
|
- destructor call type: rrfFreeInstance
|
|
- type cast
|
|
- class of
|
|
- class method, property, var, const
|
|
- class-of.constructor
|
|
- class-of typecast upwards/downwards
|
|
- class-of option to allow is-operator
|
|
- typecast Self in class method upwards/downwards
|
|
- property with params
|
|
- default property
|
|
- visibility, override: warn and fix if lower
|
|
- events, proc type of object
|
|
- sealed
|
|
- with..do
|
|
- enums - TPasEnumType, TPasEnumValue
|
|
- propagate to parent scopes
|
|
- function ord(): integer
|
|
- function low(ordinal): ordinal
|
|
- function high(ordinal): ordinal
|
|
- function pred(ordinal): ordinal
|
|
- function high(ordinal): ordinal
|
|
- cast integer to enum
|
|
- sets - TPasSetType
|
|
- set of char
|
|
- set of integer
|
|
- set of boolean
|
|
- set of enum
|
|
- ranges 'a'..'z' 2..5
|
|
- operators: +, -, *, ><, <=, >=
|
|
- in-operator
|
|
- assign operators: +=, -=, *=
|
|
- include(), exclude()
|
|
- typed const: check expr type
|
|
- function length(const array or string): integer
|
|
- procedure setlength(var array or string; newlength: integer)
|
|
- ranges TPasRangeType
|
|
- procedure exit, procedure exit(const function result)
|
|
- check if types only refer types+const
|
|
- check const expression types, e.g. bark on "const c:string=3;"
|
|
- procedure inc/dec(var ordinal; decr: ordinal = 1)
|
|
- function Assigned(Pointer or Class or Class-Of): boolean
|
|
- arrays TPasArrayType
|
|
- TPasEnumType, char, integer, range
|
|
- low, high, length, setlength, assigned
|
|
- function concat(array1,array2,...): array
|
|
- function copy(array): array, copy(a,start), copy(a,start,end)
|
|
- insert(item; var array; index: integer)
|
|
- delete(var array; start, count: integer)
|
|
- element
|
|
- multi dimensional
|
|
- const
|
|
- open array, override, pass array literal, pass var
|
|
- type cast array to arrays with same dimensions and compatible element type
|
|
- check if var initexpr fits vartype: var a: type = expr;
|
|
- built-in functions high, low for range types
|
|
- procedure type
|
|
- call
|
|
- as function result
|
|
- as parameter
|
|
- Delphi without @
|
|
- FPC equal and not equal
|
|
- "is nested"
|
|
- bark on arguments access mismatch
|
|
- function without params: mark if call or address, rrfImplicitCallWithoutParams
|
|
- procedure break, procedure continue
|
|
- built-in functions pred, succ for range type and enums
|
|
- untyped parameters
|
|
- built-in procedure str(const boolean|integer|enumvalue|classinstance,var s: string)
|
|
- pointer TPasPointerType
|
|
- nil, assigned(), typecast, class, classref, dynarray, procvar
|
|
|
|
ToDo:
|
|
- fix slow lookup declaration proc in PParser
|
|
- fail to write a loop var inside the loop
|
|
- warn: create class with abstract methods
|
|
- classes - TPasClassType
|
|
- nested var, const
|
|
- nested types
|
|
- check if constant is longint or int64
|
|
- for..in..do
|
|
- records - TPasRecordType,
|
|
- const TRecordValues
|
|
- function default(record type): record
|
|
- pointer of record
|
|
- proc: check if forward and impl default values match
|
|
- call array of proc without ()
|
|
- pointer type, ^type, @ operator, [] operator
|
|
- type alias type
|
|
- object
|
|
- interfaces
|
|
- implements, supports
|
|
- TPasResString
|
|
- generics, nested param lists
|
|
- dotted unitnames
|
|
- type helpers
|
|
- record/class helpers
|
|
- generics
|
|
- operator overload
|
|
- is nested
|
|
- TPasFileType
|
|
- labels
|
|
- many more: search for "ToDo:"
|
|
|
|
Debug flags: -d<x>
|
|
VerbosePasResolver
|
|
|
|
Notes:
|
|
Functions and function types without parameters:
|
|
property P read f; // use function f, not its result
|
|
f. // implicit resolve f once if param less function or function type
|
|
f[] // implicit resolve f once if a param less function or function type
|
|
@f; use function f, not its result
|
|
@p.f; @ operator applies to f, not p
|
|
@f(); @ operator applies to result of f
|
|
f(); use f's result
|
|
FuncVar:=Func; if mode=objfpc: incompatible
|
|
if mode=delphi: implicit addr of function f
|
|
if f=g then : can implicit resolve each side once
|
|
p(f), f as var parameter: can implicit
|
|
}
|
|
unit PasResolver;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$inline on}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, contnrs, PasTree, PParser, PScanner;
|
|
|
|
const
|
|
ParserMaxEmbeddedColumn = 2048;
|
|
ParserMaxEmbeddedRow = $7fffffff div ParserMaxEmbeddedColumn;
|
|
|
|
// message numbers
|
|
const
|
|
nIdentifierNotFound = 3001;
|
|
nNotYetImplemented = 3002;
|
|
nIllegalQualifier = 3003;
|
|
nSyntaxErrorExpectedButFound = 3004;
|
|
nWrongNumberOfParametersForCallTo = 3005;
|
|
nIncompatibleTypeArgNo = 3006;
|
|
nIncompatibleTypeArgNoVarParamMustMatchExactly = 3007;
|
|
nVariableIdentifierExpected = 3008;
|
|
nDuplicateIdentifier = 3009;
|
|
nXExpectedButYFound = 3010;
|
|
nAncestorCycleDetected = 3011;
|
|
nCantUseForwardDeclarationAsAncestor = 3012;
|
|
nCantDetermineWhichOverloadedFunctionToCall = 3013;
|
|
nForwardTypeNotResolved = 3014;
|
|
nForwardProcNotResolved = 3015;
|
|
nInvalidXModifierY = 3016;
|
|
nAbstractMethodsMustNotHaveImplementation = 3017;
|
|
nCallingConventionMismatch = 3018;
|
|
nResultTypeMismatchExpectedButFound = 3019;
|
|
nFunctionHeaderMismatchForwardVarName = 3020;
|
|
nFunctionHidesIdentifier = 3021;
|
|
nNoMethodInAncestorToOverride = 3022;
|
|
nInheritedOnlyWorksInMethods = 3023;
|
|
nInheritedNeedsAncestor = 3024;
|
|
nNoPropertyFoundToOverride = 3025;
|
|
nExprTypeMustBeClassOrRecordTypeGot = 3026;
|
|
nPropertyNotWritable = 3027;
|
|
nIncompatibleTypesGotExpected = 3028;
|
|
nTypesAreNotRelated = 3029;
|
|
nAbstractMethodsCannotBeCalledDirectly = 3030;
|
|
nMissingParameterX = 3031;
|
|
nCannotAccessThisMemberFromAX = 3032;
|
|
nInOperatorExpectsSetElementButGot = 3033;
|
|
nWrongNumberOfParametersForTypeCast = 3034;
|
|
nIllegalTypeConversionTo = 3035;
|
|
nConstantExpressionExpected = 3036;
|
|
nLeftSideOfIsOperatorExpectsAClassButGot = 3037;
|
|
nNotReadable = 3038;
|
|
nClassPropertyAccessorMustBeStatic = 3039;
|
|
nClassPropertyAccessorMustNotBeStatic = 3040;
|
|
nOnlyOneDefaultPropertyIsAllowed = 3041;
|
|
nWrongNumberOfParametersForArray = 3042;
|
|
nCantAssignValuesToAnAddress = 3043;
|
|
nIllegalExpression = 3044;
|
|
nCantAccessPrivateMember = 3045;
|
|
nMustBeInsideALoop = 3046;
|
|
nExpectXArrayElementsButFoundY = 3047;
|
|
nCannotCreateADescendantOfTheSealedClass = 3048;
|
|
nAncestorIsNotExternal = 3049;
|
|
nVirtualMethodXHasLowerVisibility = 3050; // FPC 3250
|
|
nExternalClassInstanceCannotAccessStaticX = 3051;
|
|
nXModifierMismatchY = 3052;
|
|
nSymbolCannotBePublished = 3053;
|
|
nCannotTypecastAType = 3054;
|
|
nTypeIdentifierExpected = 3055;
|
|
nCannotNestAnonymousX = 3056;
|
|
|
|
// resourcestring patterns of messages
|
|
resourcestring
|
|
sIdentifierNotFound = 'identifier not found "%s"';
|
|
sNotYetImplemented = 'not yet implemented: %s';
|
|
sIllegalQualifier = 'illegal qualifier "%s"';
|
|
sSyntaxErrorExpectedButFound = 'Syntax error, "%s" expected but "%s" found';
|
|
sWrongNumberOfParametersForCallTo = 'Wrong number of parameters specified for call to "%s"';
|
|
sIncompatibleTypeArgNo = 'Incompatible type arg no. %s: Got "%s", expected "%s"';
|
|
sIncompatibleTypeArgNoVarParamMustMatchExactly = 'Incompatible type arg no. %s: Got "%s", expected "%s". Var param must match exactly.';
|
|
sVariableIdentifierExpected = 'Variable identifier expected';
|
|
sDuplicateIdentifier = 'Duplicate identifier "%s" at %s';
|
|
sXExpectedButYFound = '%s expected, but %s found';
|
|
sAncestorCycleDetected = 'Ancestor cycle detected';
|
|
sCantUseForwardDeclarationAsAncestor = 'Can''t use forward declaration "%s" as ancestor';
|
|
sCantDetermineWhichOverloadedFunctionToCall = 'Can''t determine which overloaded function to call';
|
|
sForwardTypeNotResolved = 'Forward type not resolved "%s"';
|
|
sForwardProcNotResolved = 'Forward %s not resolved "%s"';
|
|
sInvalidXModifierY = 'Invalid %s modifier %s';
|
|
sAbstractMethodsMustNotHaveImplementation = 'Abstract method must not have an implementation.';
|
|
sCallingConventionMismatch = 'Calling convention mismatch';
|
|
sResultTypeMismatchExpectedButFound = 'Result type mismatch, expected %s, but found %s';
|
|
sFunctionHeaderMismatchForwardVarName = 'function header "%s" doesn''t match forward : var name changes %s => %s';
|
|
sFunctionHidesIdentifier = 'function hides identifier "%s" at "%s"';
|
|
sNoMethodInAncestorToOverride = 'There is no method in an ancestor class to be overridden "%s"';
|
|
sInheritedOnlyWorksInMethods = 'Inherited works only in methods';
|
|
sInheritedNeedsAncestor = 'inherited needs an ancestor';
|
|
sNoPropertyFoundToOverride = 'No property found to override';
|
|
sExprTypeMustBeClassOrRecordTypeGot = 'Expression type must be class or record type, got %s';
|
|
sPropertyNotWritable = 'No member is provided to access property';
|
|
sIncompatibleTypesGotExpected = 'Incompatible types: got "%s" expected "%s"';
|
|
sTypesAreNotRelated = 'Types are not related';
|
|
sAbstractMethodsCannotBeCalledDirectly = 'Abstract methods cannot be called directly';
|
|
sMissingParameterX = 'Missing parameter %s';
|
|
sCannotAccessThisMemberFromAX = 'Cannot access this member from a %s';
|
|
sInOperatorExpectsSetElementButGot = 'the in-operator expects a set element, but got %s';
|
|
sWrongNumberOfParametersForTypeCast = 'wrong number of parameters for type cast to %s';
|
|
sIllegalTypeConversionTo = 'Illegal type conversion: "%s" to "%s"';
|
|
sConstantExpressionExpected = 'Constant expression expected';
|
|
sLeftSideOfIsOperatorExpectsAClassButGot = 'left side of is-operator expects a class, but got %s';
|
|
sNotReadable = 'not readable';
|
|
sClassPropertyAccessorMustBeStatic = 'class property accessor must be static';
|
|
sClassPropertyAccessorMustNotBeStatic = 'class property accessor must not be static';
|
|
sOnlyOneDefaultPropertyIsAllowed = 'Only one default property is allowed';
|
|
sWrongNumberOfParametersForArray = 'Wrong number of parameters for array';
|
|
sCantAssignValuesToAnAddress = 'Can''t assign values to an address';
|
|
sIllegalExpression = 'Illegal expression';
|
|
sCantAccessPrivateMember = 'Can''t access %s member %s';
|
|
sMustBeInsideALoop = '%s must be inside a loop';
|
|
sExpectXArrayElementsButFoundY = 'Expect %s array elements, but found %s';
|
|
sCannotCreateADescendantOfTheSealedClass = 'Cannot create a descendant of the sealed class "%s"';
|
|
sAncestorIsNotExternal = 'Ancestor "%s" is not external';
|
|
sVirtualMethodXHasLowerVisibility = 'Virtual method "%s" has a lower visibility (%s) than parent class %s (%s)';
|
|
sExternalClassInstanceCannotAccessStaticX = 'External class instance cannot access static %s';
|
|
sXModifierMismatchY = '%s modifier "%s" mismatch';
|
|
sSymbolCannotBePublished = 'Symbol cannot be published';
|
|
sCannotTypecastAType = 'Cannot type cast a type';
|
|
sTypeIdentifierExpected = 'Type identifier expected';
|
|
sCannotNestAnonymousX = 'Cannot nest anonymous %s';
|
|
|
|
type
|
|
TResolverBaseType = (
|
|
btNone, // undefined
|
|
btCustom, // provided by descendant resolver
|
|
btContext, // a class or record
|
|
btModule,
|
|
btUntyped, // TPasArgument without ArgType
|
|
btChar, // char
|
|
btWideChar, // widechar
|
|
btString, // string
|
|
btAnsiString, // ansistring
|
|
btShortString, // shortstring
|
|
btWideString, // widestring
|
|
btUnicodeString,// unicodestring
|
|
btReal, // real platform, single or double
|
|
btSingle, // single 1.5E-45..3.4E38, digits 7-8, bytes 4
|
|
btDouble, // double 5.0E-324..1.7E308, digits 15-16, bytes 8
|
|
btExtended, // extended platform, double or 1.9E-4932..1.1E4932, digits 19-20, bytes 10
|
|
btCExtended, // cextended
|
|
btComp, // comp -2E64+1..2E63-1, digits 19-20, bytes 8
|
|
btCurrency, // currency ?, bytes 8
|
|
btBoolean, // boolean
|
|
btByteBool, // bytebool true=not zero
|
|
btWordBool, // wordbool true=not zero
|
|
btLongBool, // longbool true=not zero
|
|
btQWordBool, // qwordbool true=not zero
|
|
btByte, // byte 0..255
|
|
btShortInt, // shortint -128..127
|
|
btWord, // word unsigned 2 bytes
|
|
btSmallInt, // smallint signed 2 bytes
|
|
btLongWord, // longword unsigned 4 bytes
|
|
btCardinal, // cardinal see longword
|
|
btLongint, // longint signed 4 bytes
|
|
btQWord, // qword 0..18446744073709551615, bytes 8
|
|
btInt64, // int64 -9223372036854775808..9223372036854775807, bytes 8
|
|
btPointer, // pointer
|
|
btFile, // file
|
|
btText, // text
|
|
btVariant, // variant
|
|
btNil, // nil = pointer, class, procedure, method, ...
|
|
btProc, // TPasProcedure
|
|
btBuiltInProc,
|
|
btSet, // [] see SubType
|
|
btRange, // a..b see SubType
|
|
btArray // (a,b,...)
|
|
);
|
|
TResolveBaseTypes = set of TResolverBaseType;
|
|
const
|
|
btAllInteger = [btComp,btCurrency,btByte,btShortInt,btWord,btSmallInt,
|
|
btLongWord,btCardinal,btLongint,btQWord,btInt64];
|
|
btAllStrings = [btString,btAnsiString,btShortString,
|
|
btWideString,btUnicodeString];
|
|
btAllStringAndChars = btAllStrings+[btChar,btWideChar];
|
|
btAllFloats = [btReal,btSingle,btDouble,btExtended,btCExtended];
|
|
btAllBooleans = [btBoolean,btByteBool,btWordBool,btLongBool,btQWordBool];
|
|
btAllStandardTypes = [
|
|
btChar,
|
|
btWideChar,
|
|
btString,
|
|
btAnsiString,
|
|
btShortString,
|
|
btWideString,
|
|
btUnicodeString,
|
|
btReal,
|
|
btSingle,
|
|
btDouble,
|
|
btExtended,
|
|
btCExtended,
|
|
btComp,
|
|
btCurrency,
|
|
btBoolean,
|
|
btByteBool,
|
|
btWordBool,
|
|
btLongBool,
|
|
btQWordBool,
|
|
btByte,
|
|
btShortInt,
|
|
btWord,
|
|
btSmallInt,
|
|
btLongWord,
|
|
btCardinal,
|
|
btLongint,
|
|
btQWord,
|
|
btInt64,
|
|
btPointer,
|
|
btFile,
|
|
btText,
|
|
btVariant
|
|
];
|
|
btArrayRangeTypes = [btBoolean,btChar,btWideChar,
|
|
btByte,btShortInt,btWord,btSmallInt,btLongWord,btCardinal,btLongint];
|
|
|
|
BaseTypeNames: array[TResolverBaseType] of shortstring =(
|
|
'None',
|
|
'Custom',
|
|
'Context',
|
|
'Module',
|
|
'Untyped',
|
|
'Char',
|
|
'WideChar',
|
|
'String',
|
|
'AnsiString',
|
|
'ShortString',
|
|
'WideString',
|
|
'UnicodeString',
|
|
'Real',
|
|
'Single',
|
|
'Double',
|
|
'Extended',
|
|
'CExtended',
|
|
'Comp',
|
|
'Currency',
|
|
'Boolean',
|
|
'ByteBool',
|
|
'WordBool',
|
|
'LongBool',
|
|
'QWordBool',
|
|
'Byte',
|
|
'ShortInt',
|
|
'Word',
|
|
'SmallInt',
|
|
'LongWord',
|
|
'Cardinal',
|
|
'Longint',
|
|
'QWord',
|
|
'Int64',
|
|
'Pointer',
|
|
'File',
|
|
'Text',
|
|
'Variant',
|
|
'Nil',
|
|
'Procedure/Function',
|
|
'BuiltInProc',
|
|
'set',
|
|
'range..',
|
|
'array literal'
|
|
);
|
|
|
|
type
|
|
TResolverBuiltInProc = (
|
|
bfCustom,
|
|
bfLength,
|
|
bfSetLength,
|
|
bfInclude,
|
|
bfExclude,
|
|
bfBreak,
|
|
bfContinue,
|
|
bfExit,
|
|
bfInc,
|
|
bfDec,
|
|
bfAssigned,
|
|
bfChr,
|
|
bfOrd,
|
|
bfLow,
|
|
bfHigh,
|
|
bfPred,
|
|
bfSucc,
|
|
bfStrProc,
|
|
bfStrFunc,
|
|
bfConcatArray,
|
|
bfCopyArray,
|
|
bfInsertArray,
|
|
bfDeleteArray,
|
|
bfTypeInfo
|
|
);
|
|
TResolverBuiltInProcs = set of TResolverBuiltInProc;
|
|
const
|
|
ResolverBuiltInProcNames: array[TResolverBuiltInProc] of shortstring = (
|
|
'Custom',
|
|
'Length',
|
|
'SetLength',
|
|
'Include',
|
|
'Exclude',
|
|
'Break',
|
|
'Continue',
|
|
'Exit',
|
|
'Inc',
|
|
'Dec',
|
|
'Assigned',
|
|
'Chr',
|
|
'Ord',
|
|
'Low',
|
|
'High',
|
|
'Pred',
|
|
'Succ',
|
|
'Str',
|
|
'Str',
|
|
'Concat',
|
|
'Copy',
|
|
'Insert',
|
|
'Delete',
|
|
'TypeInfo'
|
|
);
|
|
bfAllStandardProcs = [Succ(bfCustom)..high(TResolverBuiltInProc)];
|
|
|
|
const
|
|
ResolverResultVar = 'Result';
|
|
|
|
type
|
|
|
|
{ EPasResolve }
|
|
|
|
EPasResolve = class(Exception)
|
|
private
|
|
FPasElement: TPasElement;
|
|
procedure SetPasElement(AValue: TPasElement);
|
|
public
|
|
Id: int64;
|
|
MsgType: TMessageType;
|
|
MsgNumber: integer;
|
|
MsgPattern: String;
|
|
Args: TMessageArgs;
|
|
SourcePos: TPasSourcePos;
|
|
destructor Destroy; override;
|
|
property PasElement: TPasElement read FPasElement write SetPasElement; // can be nil!
|
|
end;
|
|
|
|
{ TResolveData - base class for data stored in TPasElement.CustomData }
|
|
|
|
TResolveData = Class(TPasElementBase)
|
|
private
|
|
FElement: TPasElement;
|
|
procedure SetElement(AValue: TPasElement);
|
|
public
|
|
Owner: TObject; // e.g. a TPasResolver
|
|
Next: TResolveData; // TPasResolver uses this for its memory chain
|
|
constructor Create; virtual;
|
|
destructor Destroy; override;
|
|
property Element: TPasElement read FElement write SetElement;// Element.CustomData=Self
|
|
end;
|
|
TResolveDataClass = class of TResolveData;
|
|
|
|
{ TUnresolvedPendingRef }
|
|
|
|
TUnresolvedPendingRef = class(TPasUnresolvedSymbolRef)
|
|
public
|
|
Element: TPasType; // TPasClassOfType or TPasPointerType
|
|
end;
|
|
|
|
TPasScope = class;
|
|
|
|
TIterateScopeElement = procedure(El: TPasElement; ElScope, StartScope: TPasScope;
|
|
Data: Pointer; var Abort: boolean) of object;
|
|
|
|
{ TPasScope -
|
|
Elements like TPasClassType use TPasScope descendants as CustomData for
|
|
their sub identifiers.
|
|
TPasResolver.Scopes has a stack of TPasScope for searching identifiers.
|
|
}
|
|
|
|
TPasScope = Class(TResolveData)
|
|
public
|
|
VisibilityContext: TPasElement; // methods sets this to a TPasClassType,
|
|
// used to check if the current context is allowed to access a
|
|
// private/protected element
|
|
class function IsStoredInElement: boolean; virtual;
|
|
class function FreeOnPop: boolean; virtual;
|
|
procedure IterateElements(const aName: string; StartScope: TPasScope;
|
|
const OnIterateElement: TIterateScopeElement; Data: Pointer;
|
|
var Abort: boolean); virtual;
|
|
procedure WriteIdentifiers(Prefix: string); virtual;
|
|
end;
|
|
TPasScopeClass = class of TPasScope;
|
|
|
|
{ TPasModuleScope }
|
|
|
|
TPasModuleScope = class(TPasScope)
|
|
public
|
|
procedure IterateElements(const aName: string; StartScope: TPasScope;
|
|
const OnIterateElement: TIterateScopeElement; Data: Pointer;
|
|
var Abort: boolean); override;
|
|
end;
|
|
|
|
TPasIdentifierKind = (
|
|
pikNone, // not yet initialized
|
|
pikBaseType, // e.g. longint
|
|
pikBuiltInProc, // e.g. High(), SetLength()
|
|
pikSimple, // simple vars, consts, types, enums
|
|
pikProc // may need parameter list with round brackets
|
|
);
|
|
TPasIdentifierKinds = set of TPasIdentifierKind;
|
|
|
|
{ TPasIdentifier }
|
|
|
|
TPasIdentifier = Class(TObject)
|
|
private
|
|
FElement: TPasElement;
|
|
procedure SetElement(AValue: TPasElement);
|
|
public
|
|
{$IFDEF VerbosePasResolver}
|
|
Owner: TObject;
|
|
{$ENDIF}
|
|
Identifier: String;
|
|
NextSameIdentifier: TPasIdentifier; // next identifier with same name
|
|
Kind: TPasIdentifierKind;
|
|
destructor Destroy; override;
|
|
property Element: TPasElement read FElement write SetElement;
|
|
end;
|
|
|
|
{ TPasIdentifierScope - elements with a list of sub identifiers }
|
|
|
|
TPasIdentifierScope = Class(TPasScope)
|
|
private
|
|
FItems: TFPHashList;
|
|
procedure InternalAdd(Item: TPasIdentifier);
|
|
procedure OnClearItem(Item, Dummy: pointer);
|
|
procedure OnWriteItem(Item, Dummy: pointer);
|
|
public
|
|
constructor Create; override;
|
|
destructor Destroy; override;
|
|
function FindLocalIdentifier(const Identifier: String): TPasIdentifier; inline;
|
|
function FindIdentifier(const Identifier: String): TPasIdentifier; virtual;
|
|
function RemoveLocalIdentifier(El: TPasElement): boolean; virtual;
|
|
function AddIdentifier(const Identifier: String; El: TPasElement;
|
|
const Kind: TPasIdentifierKind): TPasIdentifier; virtual;
|
|
function FindElement(const aName: string): TPasElement;
|
|
procedure IterateLocalElements(const aName: string; StartScope: TPasScope;
|
|
const OnIterateElement: TIterateScopeElement; Data: Pointer;
|
|
var Abort: boolean);
|
|
procedure IterateElements(const aName: string; StartScope: TPasScope;
|
|
const OnIterateElement: TIterateScopeElement; Data: Pointer;
|
|
var Abort: boolean); override;
|
|
procedure WriteIdentifiers(Prefix: string); override;
|
|
end;
|
|
|
|
{ TPasDefaultScope - root scope }
|
|
|
|
TPasDefaultScope = class(TPasIdentifierScope)
|
|
public
|
|
class function IsStoredInElement: boolean; override;
|
|
end;
|
|
|
|
{ TPasSectionScope - e.g. interface, implementation, program, library }
|
|
|
|
TPasSectionScope = Class(TPasIdentifierScope)
|
|
public
|
|
UsesList: TFPList; // list of TPasSectionScope
|
|
constructor Create; override;
|
|
destructor Destroy; override;
|
|
function FindIdentifier(const Identifier: String): TPasIdentifier; override;
|
|
procedure IterateElements(const aName: string; StartScope: TPasScope;
|
|
const OnIterateElement: TIterateScopeElement; Data: Pointer;
|
|
var Abort: boolean); override;
|
|
procedure WriteIdentifiers(Prefix: string); override;
|
|
end;
|
|
|
|
{ TPasEnumTypeScope }
|
|
|
|
TPasEnumTypeScope = Class(TPasIdentifierScope)
|
|
public
|
|
CanonicalSet: TPasSetType;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ TPasRecordScope }
|
|
|
|
TPasRecordScope = Class(TPasIdentifierScope)
|
|
end;
|
|
|
|
TPasClassScopeFlag = (
|
|
pcsfAncestorResolved,
|
|
pcsfSealed
|
|
);
|
|
TPasClassScopeFlags = set of TPasClassScopeFlag;
|
|
|
|
{ TPasClassScope }
|
|
|
|
TPasClassScope = Class(TPasIdentifierScope)
|
|
public
|
|
AncestorScope: TPasClassScope;
|
|
DirectAncestor: TPasType; // TPasClassType or TPasAliasType or TPasTypeAliasType
|
|
DefaultProperty: TPasProperty;
|
|
Flags: TPasClassScopeFlags;
|
|
function FindIdentifier(const Identifier: String): TPasIdentifier; override;
|
|
procedure IterateElements(const aName: string; StartScope: TPasScope;
|
|
const OnIterateElement: TIterateScopeElement; Data: Pointer;
|
|
var Abort: boolean); override;
|
|
procedure WriteIdentifiers(Prefix: string); override;
|
|
end;
|
|
TPasClassScopeClass = class of TPasClassScope;
|
|
|
|
{ TPasProcedureScope }
|
|
|
|
TPasProcedureScope = Class(TPasIdentifierScope)
|
|
public
|
|
DeclarationProc: TPasProcedure; // the corresponding forward declaration
|
|
ImplProc: TPasProcedure; // the corresponding proc with Body
|
|
OverriddenProc: TPasProcedure; // if IsOverride then this is the ancestor proc (virtual or override)
|
|
ClassScope: TPasClassScope;
|
|
SelfArg: TPasArgument;
|
|
function FindIdentifier(const Identifier: String): TPasIdentifier; override;
|
|
procedure IterateElements(const aName: string; StartScope: TPasScope;
|
|
const OnIterateElement: TIterateScopeElement; Data: Pointer;
|
|
var Abort: boolean); override;
|
|
procedure WriteIdentifiers(Prefix: string); override;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ TPasPropertyScope }
|
|
|
|
TPasPropertyScope = Class(TPasIdentifierScope)
|
|
public
|
|
AncestorProp: TPasProperty;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ TPasExceptOnScope }
|
|
|
|
TPasExceptOnScope = Class(TPasIdentifierScope)
|
|
end;
|
|
|
|
TPasWithScope = class;
|
|
|
|
TPasWithExprScopeFlag = (
|
|
wesfNeedTmpVar,
|
|
wesfOnlyTypeMembers,
|
|
wesfConstParent
|
|
);
|
|
TPasWithExprScopeFlags = set of TPasWithExprScopeFlag;
|
|
|
|
{ TPasWithExprScope }
|
|
|
|
TPasWithExprScope = Class(TPasScope)
|
|
public
|
|
WithScope: TPasWithScope; // owner
|
|
Index: integer;
|
|
Expr: TPasExpr;
|
|
Scope: TPasScope;
|
|
Flags: TPasWithExprScopeFlags;
|
|
class function IsStoredInElement: boolean; override;
|
|
class function FreeOnPop: boolean; override;
|
|
procedure IterateElements(const aName: string; StartScope: TPasScope;
|
|
const OnIterateElement: TIterateScopeElement; Data: Pointer;
|
|
var Abort: boolean); override;
|
|
procedure WriteIdentifiers(Prefix: string); override;
|
|
end;
|
|
TPasWithExprScopeClass = class of TPasWithExprScope;
|
|
|
|
{ TPasWithScope }
|
|
|
|
TPasWithScope = Class(TPasScope)
|
|
public
|
|
// Element is the TPasImplWithDo
|
|
ExpressionScopes: TObjectList; // list of TPasWithExprScope
|
|
constructor Create; override;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ TPasSubScope - base class for sub scopes aka dotted scopes }
|
|
|
|
TPasSubScope = Class(TPasIdentifierScope)
|
|
public
|
|
class function IsStoredInElement: boolean; override;
|
|
end;
|
|
|
|
{ TPasIterateFilterData }
|
|
|
|
TPasIterateFilterData = record
|
|
OnIterate: TIterateScopeElement;
|
|
Data: Pointer;
|
|
end;
|
|
PPasIterateFilterData = ^TPasIterateFilterData;
|
|
|
|
{ TPasModuleDotScope - scope for searching unitname.<identifier> }
|
|
|
|
TPasModuleDotScope = Class(TPasSubScope)
|
|
private
|
|
FModule: TPasModule;
|
|
procedure OnInternalIterate(El: TPasElement; ElScope, StartScope: TPasScope;
|
|
Data: Pointer; var Abort: boolean);
|
|
procedure SetModule(AValue: TPasModule);
|
|
public
|
|
InterfaceScope: TPasSectionScope;
|
|
ImplementationScope: TPasSectionScope;
|
|
destructor Destroy; override;
|
|
function FindIdentifier(const Identifier: String): TPasIdentifier; override;
|
|
procedure IterateElements(const aName: string; StartScope: TPasScope;
|
|
const OnIterateElement: TIterateScopeElement; Data: Pointer;
|
|
var Abort: boolean); override;
|
|
procedure WriteIdentifiers(Prefix: string); override;
|
|
property Module: TPasModule read FModule write SetModule;
|
|
end;
|
|
|
|
{ TPasDotIdentifierScope }
|
|
|
|
TPasDotIdentifierScope = Class(TPasSubScope)
|
|
public
|
|
IdentifierScope: TPasIdentifierScope;
|
|
OnlyTypeMembers: boolean; // true=only class var/procs, false=default=all
|
|
ConstParent: boolean;
|
|
function FindIdentifier(const Identifier: String): TPasIdentifier; override;
|
|
procedure IterateElements(const aName: string; StartScope: TPasScope;
|
|
const OnIterateElement: TIterateScopeElement; Data: Pointer;
|
|
var Abort: boolean); override;
|
|
procedure WriteIdentifiers(Prefix: string); override;
|
|
end;
|
|
|
|
{ TPasDotRecordScope - used for aRecord.subidentifier }
|
|
|
|
TPasDotRecordScope = Class(TPasDotIdentifierScope)
|
|
end;
|
|
|
|
{ TPasDotEnumTypeScope - used for EnumType.EnumValue }
|
|
|
|
TPasDotEnumTypeScope = Class(TPasDotIdentifierScope)
|
|
end;
|
|
|
|
{ TPasDotClassScope - used for aClass.subidentifier }
|
|
|
|
TPasDotClassScope = Class(TPasDotIdentifierScope)
|
|
private
|
|
FClassScope: TPasClassScope;
|
|
procedure SetClassScope(AValue: TPasClassScope);
|
|
public
|
|
InheritedExpr: boolean; // this is 'inherited <name>' instead of '.<name'
|
|
property ClassScope: TPasClassScope read FClassScope write SetClassScope;
|
|
end;
|
|
|
|
TResolvedReferenceFlag = (
|
|
rrfDotScope, // found reference via a dot scope (TPasDotIdentifierScope)
|
|
rrfImplicitCallWithoutParams, // a TPrimitiveExpr is an implicit call without params
|
|
rrfNewInstance, // constructor call (without it call constructor as normal method)
|
|
rrfFreeInstance, // destructor call (without it call destructor as normal method)
|
|
rrfVMT, // use VMT for call
|
|
rrfConstInherited // parent is const and children are too
|
|
);
|
|
TResolvedReferenceFlags = set of TResolvedReferenceFlag;
|
|
|
|
{ TResolvedRefContext }
|
|
|
|
TResolvedRefContext = Class
|
|
end;
|
|
|
|
TResolvedRefAccess = (
|
|
rraNone,
|
|
rraRead, // expression is read
|
|
rraAssign, // expression is LHS assign
|
|
rraReadAndAssign, // expression is LHS +=, -=, *=, /=
|
|
rraVarParam, // expression is passed to a var parameter
|
|
rraOutParam, // expression is passed to an out parameter
|
|
rraParamToUnknownProc // used as param, before knowing what overladed proc to call,
|
|
// will later be changed to rraRead, rraVarParam, rraOutParam
|
|
);
|
|
TPRResolveVarAccesses = set of TResolvedRefAccess;
|
|
|
|
{ TResolvedReference - CustomData for normal references }
|
|
|
|
TResolvedReference = Class(TResolveData)
|
|
private
|
|
FDeclaration: TPasElement;
|
|
procedure SetDeclaration(AValue: TPasElement);
|
|
public
|
|
Flags: TResolvedReferenceFlags;
|
|
Access: TResolvedRefAccess;
|
|
Context: TResolvedRefContext;
|
|
WithExprScope: TPasWithExprScope;// if set, this reference used a With-block expression.
|
|
destructor Destroy; override;
|
|
property Declaration: TPasElement read FDeclaration write SetDeclaration;
|
|
end;
|
|
|
|
{ TResolvedRefCtxConstructor }
|
|
|
|
TResolvedRefCtxConstructor = Class(TResolvedRefContext)
|
|
public
|
|
Typ: TPasType; // e.g. TPasClassType
|
|
end;
|
|
|
|
TPasResolverResultFlag = (
|
|
rrfReadable,
|
|
rrfWritable,
|
|
rrfAssignable, // not writable in general, e.g. aString[1]:=
|
|
rrfCanBeStatement
|
|
);
|
|
TPasResolverResultFlags = set of TPasResolverResultFlag;
|
|
|
|
{ TPasResolverResult }
|
|
|
|
TPasResolverResult = record
|
|
BaseType: TResolverBaseType;
|
|
SubType: TResolverBaseType; // for btSet and btRange
|
|
IdentEl: TPasElement; // if set then this specific identifier is the value, can be a type
|
|
TypeEl: TPasType; // can be nil for const expression
|
|
ExprEl: TPasExpr;
|
|
Flags: TPasResolverResultFlags;
|
|
end;
|
|
PPasResolvedElement = ^TPasResolverResult;
|
|
|
|
TPasResolverComputeFlag = (
|
|
rcSkipTypeAlias,
|
|
rcNoImplicitProc, // do not call a function without params, includes rcNoImplicitProcType
|
|
rcNoImplicitProcType, // do not call a proc type without params
|
|
rcConstant, // resolve a constant expresson
|
|
rcType // resolve a type expression
|
|
);
|
|
TPasResolverComputeFlags = set of TPasResolverComputeFlag;
|
|
|
|
TResElDataBuiltInSymbol = Class(TResolveData)
|
|
public
|
|
end;
|
|
|
|
{ TResElDataBaseType - CustomData for compiler built-in types (TPasUnresolvedSymbolRef), e.g. longint }
|
|
|
|
TResElDataBaseType = Class(TResElDataBuiltInSymbol)
|
|
public
|
|
BaseType: TResolverBaseType;
|
|
end;
|
|
TResElDataBaseTypeClass = class of TResElDataBaseType;
|
|
|
|
TResElDataBuiltInProc = Class;
|
|
|
|
TOnGetCallCompatibility = function(Proc: TResElDataBuiltInProc;
|
|
Exp: TPasExpr; RaiseOnError: boolean): integer of object;
|
|
TOnGetCallResult = procedure(Proc: TResElDataBuiltInProc; Params: TParamsExpr;
|
|
out ResolvedEl: TPasResolverResult) of object;
|
|
TOnFinishParamsExpr = procedure(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr) of object;
|
|
|
|
TBuiltInProcFlag = (
|
|
bipfCanBeStatement // a call is enough for a simple statement
|
|
);
|
|
TBuiltInProcFlags = set of TBuiltInProcFlag;
|
|
|
|
{ TResElDataBuiltInProc - TPasUnresolvedSymbolRef(aType).CustomData for compiler built-in procs like 'length' }
|
|
|
|
TResElDataBuiltInProc = Class(TResElDataBuiltInSymbol)
|
|
public
|
|
Proc: TPasUnresolvedSymbolRef;
|
|
Signature: string;
|
|
BuiltIn: TResolverBuiltInProc;
|
|
GetCallCompatibility: TOnGetCallCompatibility;
|
|
GetCallResult: TOnGetCallResult;
|
|
FinishParamsExpression: TOnFinishParamsExpr;
|
|
Flags: TBuiltInProcFlags;
|
|
end;
|
|
|
|
{ TPRFindData }
|
|
|
|
TPRFindData = record
|
|
ErrorPosEl: TPasElement;
|
|
Found: TPasElement;
|
|
ElScope: TPasScope; // Where Found was found
|
|
StartScope: TPasScope; // where the searched started
|
|
end;
|
|
PPRFindData = ^TPRFindData;
|
|
|
|
TPasResolverOption = (
|
|
proFixCaseOfOverrides, // fix Name of overriding procs to the overriden proc
|
|
proClassPropertyNonStatic, // class property accessor must be non static
|
|
proPropertyAsVarParam, // allows to pass a property as a var/out argument
|
|
proClassOfIs, // class-of supports is and as operator
|
|
proExtClassInstanceNoTypeMembers, // class members of external class cannot be accessed by instance
|
|
proOpenAsDynArrays, // open arrays work like dynamic arrays
|
|
proProcTypeWithoutIsNested, // proc types can use nested procs without 'is nested'
|
|
proMethodAddrAsPointer // can assign @method to a pointer
|
|
);
|
|
TPasResolverOptions = set of TPasResolverOption;
|
|
|
|
{ TPasResolver }
|
|
|
|
TPasResolver = Class(TPasTreeContainer)
|
|
private
|
|
type
|
|
TResolveDataListKind = (lkBuiltIn,lkModule);
|
|
procedure ClearResolveDataList(Kind: TResolveDataListKind);
|
|
private
|
|
FAnonymousElTypePostfix: String;
|
|
FBaseTypes: array[TResolverBaseType] of TPasUnresolvedSymbolRef;
|
|
FBaseTypeStringIndex: TResolverBaseType;
|
|
FDefaultScope: TPasDefaultScope;
|
|
FLastCreatedData: array[TResolveDataListKind] of TResolveData;
|
|
FLastElement: TPasElement;
|
|
FLastMsg: string;
|
|
FLastMsgArgs: TMessageArgs;
|
|
FLastMsgElement: TPasElement;
|
|
FLastMsgId: int64;
|
|
FLastMsgNumber: integer;
|
|
FLastMsgPattern: string;
|
|
FLastMsgType: TMessageType;
|
|
FLastSourcePos: TPasSourcePos;
|
|
FOptions: TPasResolverOptions;
|
|
FPendingForwards: TFPList; // list of TPasElement needed to check for forward procs
|
|
FRootElement: TPasElement;
|
|
FScopeClass_Class: TPasClassScopeClass;
|
|
FScopeClass_WithExpr: TPasWithExprScopeClass;
|
|
FScopeCount: integer;
|
|
FScopes: array of TPasScope; // stack of scopes
|
|
FStoreSrcColumns: boolean;
|
|
FSubScopeCount: integer;
|
|
FSubScopes: array of TPasScope; // stack of scopes
|
|
FTopScope: TPasScope;
|
|
function GetBaseTypes(bt: TResolverBaseType): TPasUnresolvedSymbolRef; inline;
|
|
function GetScopes(Index: integer): TPasScope; inline;
|
|
protected
|
|
const
|
|
cIncompatible = High(integer);
|
|
cExact = 0;
|
|
type
|
|
TFindCallElData = record
|
|
Params: TParamsExpr;
|
|
Found: TPasElement; // TPasProcedure or TPasUnresolvedSymbolRef(built in proc) or TPasType (typecast)
|
|
ElScope, StartScope: TPasScope;
|
|
Distance: integer; // compatibility distance
|
|
Count: integer;
|
|
List: TFPList; // if not nil then collect all found elements here
|
|
end;
|
|
PFindCallElData = ^TFindCallElData;
|
|
|
|
TFindOverloadProcData = record
|
|
Proc: TPasProcedure;
|
|
Args: TFPList; // List of TPasArgument objects
|
|
OnlyScope: TPasScope;
|
|
Found: TPasProcedure;
|
|
ElScope, StartScope: TPasScope;
|
|
FoundNonProc: TPasElement;
|
|
end;
|
|
PFindOverloadProcData = ^TFindOverloadProcData;
|
|
|
|
procedure OnFindFirstElement(El: TPasElement; ElScope, StartScope: TPasScope;
|
|
FindFirstElementData: Pointer; var Abort: boolean); virtual;
|
|
procedure OnFindCallElements(El: TPasElement; ElScope, StartScope: TPasScope;
|
|
FindProcsData: Pointer; var Abort: boolean); virtual;
|
|
procedure OnFindOverloadProc(El: TPasElement; ElScope, StartScope: TPasScope;
|
|
FindOverloadData: Pointer; var Abort: boolean); virtual;
|
|
protected
|
|
procedure SetCurrentParser(AValue: TPasParser); override;
|
|
procedure CheckTopScope(ExpectedClass: TPasScopeClass);
|
|
function AddIdentifier(Scope: TPasIdentifierScope;
|
|
const aName: String; El: TPasElement;
|
|
const Kind: TPasIdentifierKind): TPasIdentifier; virtual;
|
|
procedure AddModule(El: TPasModule); virtual;
|
|
procedure AddSection(El: TPasSection); virtual;
|
|
procedure AddType(El: TPasType); virtual;
|
|
procedure AddRecordType(El: TPasRecordType); virtual;
|
|
procedure AddClassType(El: TPasClassType); virtual;
|
|
procedure AddVariable(El: TPasVariable); virtual;
|
|
procedure AddEnumType(El: TPasEnumType); virtual;
|
|
procedure AddEnumValue(El: TPasEnumValue); virtual;
|
|
procedure AddProperty(El: TPasProperty); virtual;
|
|
procedure AddProcedure(El: TPasProcedure); virtual;
|
|
procedure AddProcedureBody(El: TProcedureBody); virtual;
|
|
procedure AddArgument(El: TPasArgument); virtual;
|
|
procedure AddFunctionResult(El: TPasResultElement); virtual;
|
|
procedure AddExceptOn(El: TPasImplExceptOn); virtual;
|
|
procedure ResolveImplBlock(Block: TPasImplBlock); virtual;
|
|
procedure ResolveImplElement(El: TPasImplElement); virtual;
|
|
procedure ResolveImplCaseOf(CaseOf: TPasImplCaseOf); virtual;
|
|
procedure ResolveImplLabelMark(Mark: TPasImplLabelMark); virtual;
|
|
procedure ResolveImplForLoop(Loop: TPasImplForLoop); virtual;
|
|
procedure ResolveImplWithDo(El: TPasImplWithDo); virtual;
|
|
procedure ResolveImplAsm(El: TPasImplAsmStatement); virtual;
|
|
procedure ResolveImplAssign(El: TPasImplAssign); virtual;
|
|
procedure ResolveImplSimple(El: TPasImplSimple); virtual;
|
|
procedure ResolveImplRaise(El: TPasImplRaise); virtual;
|
|
procedure ResolveExpr(El: TPasExpr; Access: TResolvedRefAccess); virtual;
|
|
procedure ResolveStatementConditionExpr(El: TPasExpr); virtual;
|
|
procedure ResolveNameExpr(El: TPasExpr; const aName: string; Access: TResolvedRefAccess); virtual;
|
|
procedure ResolveInherited(El: TInheritedExpr; Access: TResolvedRefAccess); virtual;
|
|
procedure ResolveInheritedCall(El: TBinaryExpr; Access: TResolvedRefAccess); virtual;
|
|
procedure ResolveBinaryExpr(El: TBinaryExpr; Access: TResolvedRefAccess); virtual;
|
|
procedure ResolveSubIdent(El: TBinaryExpr; Access: TResolvedRefAccess); virtual;
|
|
procedure ResolveParamsExpr(Params: TParamsExpr; Access: TResolvedRefAccess); virtual;
|
|
procedure ResolveFuncParamsExpr(Params: TParamsExpr; Access: TResolvedRefAccess); virtual;
|
|
procedure ResolveArrayParamsExpr(Params: TParamsExpr; Access: TResolvedRefAccess); virtual;
|
|
procedure ResolveArrayParamsArgs(Params: TParamsExpr;
|
|
const ResolvedValue: TPasResolverResult; Access: TResolvedRefAccess); virtual;
|
|
function ResolveBracketOperatorClass(Params: TParamsExpr;
|
|
const ResolvedValue: TPasResolverResult; ClassScope: TPasClassScope;
|
|
Access: TResolvedRefAccess): boolean; virtual;
|
|
procedure ResolveSetParamsExpr(Params: TParamsExpr); virtual;
|
|
procedure ResolveArrayValues(El: TArrayValues); virtual;
|
|
procedure SetResolvedRefAccess(Expr: TPasExpr; Ref: TResolvedReference;
|
|
Access: TResolvedRefAccess); virtual;
|
|
procedure AccessExpr(Expr: TPasExpr; Access: TResolvedRefAccess);
|
|
procedure FinishModule(CurModule: TPasModule); virtual;
|
|
procedure FinishUsesList; virtual;
|
|
procedure FinishTypeSection(El: TPasDeclarations); virtual;
|
|
procedure FinishTypeDef(El: TPasType); virtual;
|
|
procedure FinishEnumType(El: TPasEnumType); virtual;
|
|
procedure FinishSetType(El: TPasSetType); virtual;
|
|
procedure FinishSubElementType(Parent, El: TPasElement); virtual;
|
|
procedure FinishRangeType(El: TPasRangeType); virtual;
|
|
procedure FinishRecordType(El: TPasRecordType); virtual;
|
|
procedure FinishClassType(El: TPasClassType); virtual;
|
|
procedure FinishClassOfType(El: TPasClassOfType); virtual;
|
|
procedure FinishArrayType(El: TPasArrayType); virtual;
|
|
procedure FinishConstDef(El: TPasConst); virtual;
|
|
procedure FinishProcedure(aProc: TPasProcedure); virtual;
|
|
procedure FinishProcedureType(El: TPasProcedureType); virtual;
|
|
procedure FinishMethodDeclHeader(Proc: TPasProcedure); virtual;
|
|
procedure FinishMethodImplHeader(ImplProc: TPasProcedure); virtual;
|
|
procedure FinishExceptOnExpr; virtual;
|
|
procedure FinishExceptOnStatement; virtual;
|
|
procedure FinishDeclaration(El: TPasElement); virtual;
|
|
procedure FinishVariable(El: TPasVariable); virtual;
|
|
procedure FinishPropertyOfClass(PropEl: TPasProperty); virtual;
|
|
procedure FinishArgument(El: TPasArgument); virtual;
|
|
procedure FinishAncestors(aClass: TPasClassType); virtual;
|
|
procedure FinishPropertyParamAccess(Params: TParamsExpr;
|
|
Prop: TPasProperty);
|
|
procedure ReplaceProcScopeImplArgsWithDeclArgs(ImplProcScope: TPasProcedureScope);
|
|
procedure CheckProcSignatureMatch(DeclProc, ImplProc: TPasProcedure);
|
|
procedure CheckPendingForwards(El: TPasElement);
|
|
procedure ComputeBinaryExpr(Bin: TBinaryExpr;
|
|
out ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
|
|
StartEl: TPasElement);
|
|
procedure ComputeArrayParams(Params: TParamsExpr;
|
|
out ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
|
|
StartEl: TPasElement);
|
|
procedure ComputeArrayParams_Class(Params: TParamsExpr;
|
|
var ResolvedEl: TPasResolverResult; ClassScope: TPasClassScope;
|
|
Flags: TPasResolverComputeFlags; StartEl: TPasElement); virtual;
|
|
procedure ComputeFuncParams(Params: TParamsExpr;
|
|
out ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
|
|
StartEl: TPasElement);
|
|
procedure ComputeSetParams(Params: TParamsExpr;
|
|
out ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
|
|
StartEl: TPasElement);
|
|
procedure CheckIsClass(El: TPasElement; const ResolvedEl: TPasResolverResult);
|
|
function CheckTypeCastClassInstanceToClass(
|
|
const FromClassRes, ToClassRes: TPasResolverResult;
|
|
ErrorEl: TPasElement): integer; virtual;
|
|
procedure CheckRangeExpr(Left, Right: TPasExpr;
|
|
out LeftResolved, RightResolved: TPasResolverResult);
|
|
procedure CheckSetElementsCompatible(Left, Right: TPasExpr;
|
|
const LeftResolved, RightResolved: TPasResolverResult);
|
|
function CheckIsOrdinal(const ResolvedEl: TPasResolverResult;
|
|
ErrorEl: TPasElement; RaiseOnError: boolean): boolean;
|
|
procedure ConvertRangeToFirstValue(var ResolvedEl: TPasResolverResult);
|
|
function IsCharLiteral(const Value: string): boolean; virtual;
|
|
function CheckBuiltInMinParamCount(Proc: TResElDataBuiltInProc; Expr: TPasExpr;
|
|
MinCount: integer; RaiseOnError: boolean): boolean;
|
|
function CheckBuiltInMaxParamCount(Proc: TResElDataBuiltInProc; Params: TParamsExpr;
|
|
MaxCount: integer; RaiseOnError: boolean): integer;
|
|
function CheckRaiseTypeArgNo(id: int64; ArgNo: integer; Param: TPasExpr;
|
|
const ParamResolved: TPasResolverResult; Expected: string; RaiseOnError: boolean): integer;
|
|
// custom types (added by descendant resolvers)
|
|
function CheckAssignCompatibilityCustom(
|
|
const LHS, RHS: TPasResolverResult; ErrorEl: TPasElement;
|
|
RaiseOnIncompatible: boolean; var Handled: boolean): integer; virtual;
|
|
function CheckEqualCompatibilityCustomType(
|
|
const LHS, RHS: TPasResolverResult; ErrorEl: TPasElement;
|
|
RaiseOnIncompatible: boolean): integer; virtual;
|
|
protected
|
|
// built-in functions
|
|
function BI_Length_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_Length_OnGetCallResult(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr; out ResolvedEl: TPasResolverResult); virtual;
|
|
function BI_SetLength_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_SetLength_OnFinishParamsExpr(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr); virtual;
|
|
function BI_InExclude_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_InExclude_OnFinishParamsExpr(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr); virtual;
|
|
function BI_Break_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
function BI_Continue_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
function BI_Exit_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
function BI_IncDec_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_IncDec_OnFinishParamsExpr(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr); virtual;
|
|
function BI_Assigned_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_Assigned_OnGetCallResult(Proc: TResElDataBuiltInProc;
|
|
{%H-}Params: TParamsExpr; out ResolvedEl: TPasResolverResult); virtual;
|
|
function BI_Chr_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_Chr_OnGetCallResult(Proc: TResElDataBuiltInProc;
|
|
{%H-}Params: TParamsExpr; out ResolvedEl: TPasResolverResult); virtual;
|
|
function BI_Ord_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_Ord_OnGetCallResult(Proc: TResElDataBuiltInProc;
|
|
{%H-}Params: TParamsExpr; out ResolvedEl: TPasResolverResult); virtual;
|
|
function BI_LowHigh_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_LowHigh_OnGetCallResult(Proc: TResElDataBuiltInProc;
|
|
{%H-}Params: TParamsExpr; out ResolvedEl: TPasResolverResult); virtual;
|
|
function BI_PredSucc_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_PredSucc_OnGetCallResult({%H-}Proc: TResElDataBuiltInProc;
|
|
{%H-}Params: TParamsExpr; out ResolvedEl: TPasResolverResult); virtual;
|
|
function BI_Str_CheckParam(IsFunc: boolean; Param: TPasExpr;
|
|
const ParamResolved: TPasResolverResult; ArgNo: integer;
|
|
RaiseOnError: boolean): integer;
|
|
function BI_StrProc_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_StrProc_OnFinishParamsExpr(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr); virtual;
|
|
function BI_StrFunc_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_StrFunc_OnGetCallResult({%H-}Proc: TResElDataBuiltInProc;
|
|
{%H-}Params: TParamsExpr; out ResolvedEl: TPasResolverResult); virtual;
|
|
function BI_ConcatArray_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_ConcatArray_OnGetCallResult({%H-}Proc: TResElDataBuiltInProc;
|
|
{%H-}Params: TParamsExpr; out ResolvedEl: TPasResolverResult); virtual;
|
|
function BI_CopyArray_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_CopyArray_OnGetCallResult({%H-}Proc: TResElDataBuiltInProc;
|
|
{%H-}Params: TParamsExpr; out ResolvedEl: TPasResolverResult); virtual;
|
|
function BI_InsertArray_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_InsertArray_OnFinishParamsExpr(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr); virtual;
|
|
function BI_DeleteArray_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_DeleteArray_OnFinishParamsExpr(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr); virtual;
|
|
function BI_TypeInfo_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
|
|
procedure BI_TypeInfo_OnGetCallResult({%H-}Proc: TResElDataBuiltInProc;
|
|
{%H-}Params: TParamsExpr; out ResolvedEl: TPasResolverResult); virtual;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Clear; virtual; // does not free built-in identifiers
|
|
// overrides of TPasTreeContainer
|
|
function CreateElement(AClass: TPTreeElement; const AName: String;
|
|
AParent: TPasElement; AVisibility: TPasMemberVisibility;
|
|
const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
|
|
overload; override;
|
|
function CreateElement(AClass: TPTreeElement; const AName: String;
|
|
AParent: TPasElement; AVisibility: TPasMemberVisibility;
|
|
const ASrcPos: TPasSourcePos): TPasElement;
|
|
overload; override;
|
|
function FindElement(const aName: String): TPasElement; override; // used by TPasParser
|
|
function FindElementWithoutParams(const AName: String; ErrorPosEl: TPasElement;
|
|
NoProcsWithArgs: boolean): TPasElement;
|
|
function FindElementWithoutParams(const AName: String; out Data: TPRFindData;
|
|
ErrorPosEl: TPasElement; NoProcsWithArgs: boolean): TPasElement;
|
|
procedure IterateElements(const aName: string;
|
|
const OnIterateElement: TIterateScopeElement; Data: Pointer;
|
|
var Abort: boolean); virtual;
|
|
procedure CheckFoundElement(const FindData: TPRFindData;
|
|
Ref: TResolvedReference); virtual;
|
|
function GetVisibilityContext: TPasElement;
|
|
procedure FinishScope(ScopeType: TPasScopeType; El: TPasElement); override;
|
|
// built in types and functions
|
|
procedure ClearBuiltInIdentifiers; virtual;
|
|
procedure AddObjFPCBuiltInIdentifiers(
|
|
const TheBaseTypes: TResolveBaseTypes = btAllStandardTypes;
|
|
const TheBaseProcs: TResolverBuiltInProcs = bfAllStandardProcs); virtual;
|
|
function AddBaseType(const aName: string; Typ: TResolverBaseType): TResElDataBaseType;
|
|
function AddCustomBaseType(const aName: string; aClass: TResElDataBaseTypeClass): TPasUnresolvedSymbolRef;
|
|
function IsBaseType(aType: TPasType; BaseType: TResolverBaseType): boolean;
|
|
function AddBuiltInProc(const aName: string; Signature: string;
|
|
const GetCallCompatibility: TOnGetCallCompatibility;
|
|
const GetCallResult: TOnGetCallResult;
|
|
const FinishParamsExpr: TOnFinishParamsExpr = nil;
|
|
const BuiltIn: TResolverBuiltInProc = bfCustom;
|
|
const Flags: TBuiltInProcFlags = []): TResElDataBuiltInProc;
|
|
// add extra TResolveData (E.CustomData) to free list
|
|
procedure AddResolveData(El: TPasElement; Data: TResolveData;
|
|
Kind: TResolveDataListKind);
|
|
function CreateReference(DeclEl, RefEl: TPasElement;
|
|
Access: TResolvedRefAccess;
|
|
FindData: PPRFindData = nil): TResolvedReference; virtual;
|
|
// scopes
|
|
function CreateScope(El: TPasElement; ScopeClass: TPasScopeClass): TPasScope; virtual;
|
|
procedure PopScope;
|
|
procedure PushScope(Scope: TPasScope); overload;
|
|
function PushScope(El: TPasElement; ScopeClass: TPasScopeClass): TPasScope; overload;
|
|
function PushModuleDotScope(aModule: TPasModule): TPasModuleDotScope;
|
|
function PushClassDotScope(var CurClassType: TPasClassType): TPasDotClassScope;
|
|
function PushRecordDotScope(CurRecordType: TPasRecordType): TPasDotRecordScope;
|
|
function PushEnumDotScope(CurEnumType: TPasEnumType): TPasDotEnumTypeScope;
|
|
procedure ResetSubScopes(out Depth: integer);
|
|
procedure RestoreSubScopes(Depth: integer);
|
|
// log and messages
|
|
class procedure UnmangleSourceLineNumber(LineNumber: integer;
|
|
out Line, Column: integer);
|
|
class function GetElementSourcePosStr(El: TPasElement): string;
|
|
procedure SetLastMsg(const id: int64; MsgType: TMessageType; MsgNumber: integer;
|
|
Const Fmt : String; Args : Array of const; PosEl: TPasElement);
|
|
procedure LogMsg(const id: int64; MsgType: TMessageType; MsgNumber: integer;
|
|
const Fmt: String; Args: Array of const; PosEl: TPasElement); overload;
|
|
procedure RaiseMsg(const Id: int64; MsgNumber: integer; const Fmt: String;
|
|
Args: Array of const; ErrorPosEl: TPasElement);
|
|
procedure RaiseNotYetImplemented(id: int64; El: TPasElement; Msg: string = ''); virtual;
|
|
procedure RaiseInternalError(id: int64; const Msg: string = '');
|
|
procedure RaiseInvalidScopeForElement(id: int64; El: TPasElement; const Msg: string = '');
|
|
procedure RaiseIdentifierNotFound(id: int64; Identifier: string; El: TPasElement);
|
|
procedure RaiseXExpectedButYFound(id: int64; const X,Y: string; El: TPasElement);
|
|
procedure RaiseConstantExprExp(id: int64; ErrorEl: TPasElement);
|
|
procedure RaiseIncompatibleTypeDesc(id: int64; MsgNumber: integer;
|
|
const Args: array of const; const GotDesc, ExpDesc: String; ErrorEl: TPasElement);
|
|
procedure RaiseIncompatibleType(id: int64; MsgNumber: integer;
|
|
const Args: array of const; GotType, ExpType: TPasType; ErrorEl: TPasElement);
|
|
procedure RaiseIncompatibleTypeRes(id: int64; MsgNumber: integer;
|
|
const Args: array of const; const GotType, ExpType: TPasResolverResult;
|
|
ErrorEl: TPasElement);
|
|
procedure WriteScopes;
|
|
// find value and type of an element
|
|
procedure ComputeElement(El: TPasElement; out ResolvedEl: TPasResolverResult;
|
|
Flags: TPasResolverComputeFlags; StartEl: TPasElement = nil);
|
|
// checking compatibilility
|
|
function IsSameType(TypeA, TypeB: TPasType): boolean; // check if it is exactly the same
|
|
function CheckCallProcCompatibility(ProcType: TPasProcedureType;
|
|
Params: TParamsExpr; RaiseOnError: boolean): integer;
|
|
function CheckCallPropertyCompatibility(PropEl: TPasProperty;
|
|
Params: TParamsExpr; RaiseOnError: boolean): integer;
|
|
function CheckCallArrayCompatibility(ArrayEl: TPasArrayType;
|
|
Params: TParamsExpr; RaiseOnError: boolean): integer;
|
|
function CheckParamCompatibility(Expr: TPasExpr; Param: TPasArgument;
|
|
ParamNo: integer; RaiseOnError: boolean): integer;
|
|
function CheckAssignCompatibilityUserType(
|
|
const LHS, RHS: TPasResolverResult; ErrorEl: TPasElement;
|
|
RaiseOnIncompatible: boolean): integer;
|
|
function CheckAssignCompatibilityArrayType(
|
|
const LHS, RHS: TPasResolverResult; ErrorEl: TPasElement;
|
|
RaiseOnIncompatible: boolean): integer;
|
|
function CheckConstArrayCompatibility(Params: TParamsExpr;
|
|
const ArrayResolved: TPasResolverResult; RaiseOnError: boolean;
|
|
Flags: TPasResolverComputeFlags; StartEl: TPasElement = nil): integer;
|
|
function CheckEqualCompatibilityUserType(
|
|
const TypeA, TypeB: TPasResolverResult; ErrorEl: TPasElement;
|
|
RaiseOnIncompatible: boolean): integer;
|
|
function CheckTypeCast(El: TPasType; Params: TParamsExpr; RaiseOnError: boolean): integer;
|
|
function CheckTypeCastRes(const FromResolved, ToResolved: TPasResolverResult;
|
|
ErrorEl: TPasElement; RaiseOnError: boolean): integer; virtual;
|
|
function CheckTypeCastArray(FromType, ToType: TPasArrayType;
|
|
ErrorEl: TPasElement; RaiseOnError: boolean): integer;
|
|
function CheckSrcIsADstType(
|
|
const ResolvedSrcType, ResolvedDestType: TPasResolverResult;
|
|
ErrorEl: TPasElement): integer;
|
|
function CheckClassIsClass(SrcType, DestType: TPasType;
|
|
ErrorEl: TPasElement): integer; virtual;
|
|
function CheckClassesAreRelated(TypeA, TypeB: TPasType;
|
|
ErrorEl: TPasElement): integer;
|
|
function CheckOverloadProcCompatibility(Proc1, Proc2: TPasProcedure): boolean;
|
|
function CheckProcTypeCompatibility(Proc1, Proc2: TPasProcedureType;
|
|
ErrorEl: TPasElement; RaiseOnIncompatible: boolean): boolean;
|
|
function CheckProcArgCompatibility(Arg1, Arg2: TPasArgument): boolean;
|
|
function CheckProcArgTypeCompatibility(Arg1, Arg2: TPasType): boolean;
|
|
function CheckCanBeLHS(const ResolvedEl: TPasResolverResult;
|
|
ErrorOnFalse: boolean; ErrorEl: TPasElement): boolean;
|
|
function CheckAssignCompatibility(const LHS, RHS: TPasElement;
|
|
RaiseOnIncompatible: boolean = true): integer;
|
|
function CheckAssignResCompatibility(const LHS, RHS: TPasResolverResult;
|
|
ErrorEl: TPasElement; RaiseOnIncompatible: boolean): integer;
|
|
function CheckEqualElCompatibility(Left, Right: TPasElement;
|
|
ErrorEl: TPasElement; RaiseOnIncompatible: boolean): integer;
|
|
function CheckEqualResCompatibility(const LHS, RHS: TPasResolverResult;
|
|
LErrorEl: TPasElement; RaiseOnIncompatible: boolean;
|
|
RErrorEl: TPasElement = nil): integer;
|
|
function ResolvedElHasValue(const ResolvedEl: TPasResolverResult): boolean;
|
|
function ResolvedElCanBeVarParam(const ResolvedEl: TPasResolverResult): boolean;
|
|
function ResolvedElIsClassInstance(const ResolvedEl: TPasResolverResult): boolean;
|
|
// uility functions
|
|
function GetPasPropertyType(El: TPasProperty): TPasType;
|
|
function GetPasPropertyAncestor(El: TPasProperty): TPasProperty;
|
|
function GetPasPropertyGetter(El: TPasProperty): TPasElement;
|
|
function GetPasPropertySetter(El: TPasProperty): TPasElement;
|
|
function GetPasPropertyStored(El: TPasProperty): TPasElement;
|
|
function GetPasClassAncestor(ClassEl: TPasClassType; SkipAlias: boolean): TPasType;
|
|
function GetLoop(El: TPasElement): TPasImplElement;
|
|
function ResolveAliasType(aType: TPasType): TPasType;
|
|
function ExprIsAddrTarget(El: TPasExpr): boolean;
|
|
function IsNameExpr(El: TPasExpr): boolean; inline; // TSelfExpr or TPrimitiveExpr with Kind=pekIdent
|
|
function GetLastExprIdentifier(El: TPasExpr): TPasExpr;
|
|
function ParentNeedsExprResult(El: TPasExpr): boolean;
|
|
function GetReference_NewInstanceClass(Ref: TResolvedReference): TPasClassType;
|
|
function IsDynArray(TypeEl: TPasType): boolean;
|
|
function IsOpenArray(TypeEl: TPasType): boolean;
|
|
function IsDynOrOpenArray(TypeEl: TPasType): boolean;
|
|
function IsClassMethod(El: TPasElement): boolean;
|
|
function IsExternalClassName(aClass: TPasClassType; const ExtName: string): boolean;
|
|
function IsProcedureType(const ResolvedEl: TPasResolverResult; HasValue: boolean): boolean;
|
|
function IsArrayType(const ResolvedEl: TPasResolverResult): boolean;
|
|
function IsTypeCast(Params: TParamsExpr): boolean;
|
|
function ProcNeedsParams(El: TPasProcedureType): boolean;
|
|
function GetRangeLength(RangeResolved: TPasResolverResult): integer;
|
|
function HasTypeInfo(El: TPasType): boolean; virtual;
|
|
public
|
|
property BaseTypes[bt: TResolverBaseType]: TPasUnresolvedSymbolRef read GetBaseTypes;
|
|
property BaseTypeStringIndex: TResolverBaseType read FBaseTypeStringIndex write FBaseTypeStringIndex;
|
|
property LastElement: TPasElement read FLastElement;
|
|
property StoreSrcColumns: boolean read FStoreSrcColumns write FStoreSrcColumns; {
|
|
If true Line and Column is mangled together in TPasElement.SourceLineNumber.
|
|
Use method UnmangleSourceLineNumber to extract. }
|
|
property Scopes[Index: integer]: TPasScope read GetScopes;
|
|
property ScopeCount: integer read FScopeCount;
|
|
property TopScope: TPasScope read FTopScope;
|
|
property RootElement: TPasElement read FRootElement;
|
|
property DefaultScope: TPasDefaultScope read FDefaultScope write FDefaultScope;
|
|
property LastMsg: string read FLastMsg write FLastMsg;
|
|
property LastMsgArgs: TMessageArgs read FLastMsgArgs write FLastMsgArgs;
|
|
property LastMsgElement: TPasElement read FLastMsgElement write FLastMsgElement;
|
|
property LastMsgId: int64 read FLastMsgId write FLastMsgId;
|
|
property LastMsgNumber: integer read FLastMsgNumber write FLastMsgNumber;
|
|
property LastMsgPattern: string read FLastMsgPattern write FLastMsgPattern;
|
|
property LastMsgType: TMessageType read FLastMsgType write FLastMsgType;
|
|
property LastSourcePos: TPasSourcePos read FLastSourcePos write FLastSourcePos;
|
|
property Options: TPasResolverOptions read FOptions write FOptions;
|
|
property ScopeClass_Class: TPasClassScopeClass read FScopeClass_Class write FScopeClass_Class;
|
|
property ScopeClass_WithExpr: TPasWithExprScopeClass read FScopeClass_WithExpr write FScopeClass_WithExpr;
|
|
property AnonymousElTypePostfix: String read FAnonymousElTypePostfix
|
|
write FAnonymousElTypePostfix; // default empty, if set, anonymous element types are named ArrayName+Postfix and added to declarations
|
|
end;
|
|
|
|
function GetObjName(o: TObject): string;
|
|
function GetProcDesc(ProcType: TPasProcedureType; UseName: boolean = true; AddPaths: boolean = false): string;
|
|
function GetTypeDesc(aType: TPasType; AddPath: boolean = false): string;
|
|
function GetTreeDesc(El: TPasElement; Indent: integer = 0): string;
|
|
function GetResolverResultDesc(const T: TPasResolverResult): string;
|
|
function GetResolverResultDescription(const T: TPasResolverResult; OnlyType: boolean = false): string;
|
|
function GetResolverResultDbg(const T: TPasResolverResult): string;
|
|
function ResolverResultFlagsToStr(const Flags: TPasResolverResultFlags): string;
|
|
procedure SetResolverIdentifier(out ResolvedType: TPasResolverResult;
|
|
BaseType: TResolverBaseType; IdentEl: TPasElement;
|
|
TypeEl: TPasType; Flags: TPasResolverResultFlags); overload;
|
|
procedure SetResolverTypeExpr(out ResolvedType: TPasResolverResult;
|
|
BaseType: TResolverBaseType; TypeEl: TPasType;
|
|
Flags: TPasResolverResultFlags); overload;
|
|
procedure SetResolverValueExpr(out ResolvedType: TPasResolverResult;
|
|
BaseType: TResolverBaseType; TypeEl: TPasType; ExprEl: TPasExpr;
|
|
Flags: TPasResolverResultFlags); overload;
|
|
function ProcNeedsImplProc(Proc: TPasProcedure): boolean;
|
|
function dbgs(const Flags: TPasResolverComputeFlags): string; overload;
|
|
function dbgs(const a: TResolvedRefAccess): string;
|
|
function dbgs(const Flags: TResolvedReferenceFlags): string; overload;
|
|
|
|
implementation
|
|
|
|
function GetObjName(o: TObject): string;
|
|
begin
|
|
if o=nil then
|
|
Result:='nil'
|
|
else if o is TPasElement then
|
|
Result:=TPasElement(o).Name+':'+o.ClassName
|
|
else
|
|
Result:=o.ClassName;
|
|
end;
|
|
|
|
function GetProcDesc(ProcType: TPasProcedureType; UseName: boolean;
|
|
AddPaths: boolean): string;
|
|
var
|
|
Args: TFPList;
|
|
i: Integer;
|
|
Arg: TPasArgument;
|
|
begin
|
|
if ProcType=nil then exit('nil');
|
|
Result:=ProcType.TypeName;
|
|
if UseName and (ProcType.Parent is TPasProcedure) then
|
|
begin
|
|
if AddPaths then
|
|
Result:=Result+' '+ProcType.Parent.FullName
|
|
else
|
|
Result:=Result+' '+ProcType.Parent.Name;
|
|
end;
|
|
Args:=ProcType.Args;
|
|
if Args.Count>0 then
|
|
begin
|
|
Result:=Result+'(';
|
|
for i:=0 to Args.Count-1 do
|
|
begin
|
|
if i>0 then Result:=Result+';';
|
|
Arg:=TPasArgument(Args[i]);
|
|
if AccessNames[Arg.Access]<>'' then
|
|
Result:=Result+AccessNames[Arg.Access];
|
|
if Arg.ArgType=nil then
|
|
Result:=Result+'untyped'
|
|
else
|
|
Result:=Result+GetTypeDesc(Arg.ArgType,AddPaths);
|
|
end;
|
|
Result:=Result+')';
|
|
end;
|
|
if ProcType.IsOfObject then
|
|
Result:=Result+' '+ProcTypeModifiers[ptmOfObject];
|
|
if ProcType.IsNested then
|
|
Result:=Result+' '+ProcTypeModifiers[ptmIsNested];
|
|
if cCallingConventions[ProcType.CallingConvention]<>'' then
|
|
Result:=Result+';'+cCallingConventions[ProcType.CallingConvention];
|
|
end;
|
|
|
|
function GetTypeDesc(aType: TPasType; AddPath: boolean): string;
|
|
|
|
function GetName: string;
|
|
var
|
|
s: String;
|
|
begin
|
|
Result:=aType.Name;
|
|
if Result='' then
|
|
Result:=aType.ElementTypeName;
|
|
if AddPath then
|
|
begin
|
|
s:=aType.FullPath;
|
|
if (s<>'') and (s<>'.') then
|
|
Result:=s+'.'+Result;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
C: TClass;
|
|
begin
|
|
if aType=nil then exit('untyped');
|
|
C:=aType.ClassType;
|
|
if (C=TPasUnresolvedSymbolRef) then
|
|
begin
|
|
Result:=GetName;
|
|
if TPasUnresolvedSymbolRef(aType).CustomData is TResElDataBuiltInProc then
|
|
Result:=Result+'()';
|
|
exit;
|
|
end
|
|
else if (C=TPasUnresolvedTypeRef) then
|
|
Result:=GetName
|
|
else
|
|
Result:=GetName;
|
|
end;
|
|
|
|
function GetTreeDesc(El: TPasElement; Indent: integer): string;
|
|
|
|
procedure LineBreak(SubIndent: integer);
|
|
begin
|
|
Inc(Indent,SubIndent);
|
|
Result:=Result+LineEnding+Space(Indent);
|
|
end;
|
|
|
|
var
|
|
i, l: Integer;
|
|
begin
|
|
if El=nil then exit('nil');
|
|
Result:=El.Name+':'+El.ClassName+'=';
|
|
if El is TPasExpr then
|
|
begin
|
|
if El.ClassType<>TBinaryExpr then
|
|
Result:=Result+OpcodeStrings[TPasExpr(El).OpCode];
|
|
if El.ClassType=TUnaryExpr then
|
|
Result:=Result+GetTreeDesc(TUnaryExpr(El).Operand,Indent)
|
|
else if El.ClassType=TBinaryExpr then
|
|
Result:=Result+GetTreeDesc(TBinaryExpr(El).left,Indent)
|
|
+OpcodeStrings[TPasExpr(El).OpCode]
|
|
+GetTreeDesc(TBinaryExpr(El).right,Indent)
|
|
else if El.ClassType=TPrimitiveExpr then
|
|
Result:=Result+TPrimitiveExpr(El).Value
|
|
else if El.ClassType=TBoolConstExpr then
|
|
Result:=Result+BoolToStr(TBoolConstExpr(El).Value,'true','false')
|
|
else if El.ClassType=TNilExpr then
|
|
Result:=Result+'nil'
|
|
else if El.ClassType=TInheritedExpr then
|
|
Result:=Result+'inherited'
|
|
else if El.ClassType=TSelfExpr then
|
|
Result:=Result+'Self'
|
|
else if El.ClassType=TParamsExpr then
|
|
begin
|
|
LineBreak(2);
|
|
Result:=Result+GetTreeDesc(TParamsExpr(El).Value,Indent)+'(';
|
|
l:=length(TParamsExpr(El).Params);
|
|
if l>0 then
|
|
begin
|
|
inc(Indent,2);
|
|
for i:=0 to l-1 do
|
|
begin
|
|
LineBreak(0);
|
|
Result:=Result+GetTreeDesc(TParamsExpr(El).Params[i],Indent);
|
|
if i<l-1 then
|
|
Result:=Result+','
|
|
end;
|
|
dec(Indent,2);
|
|
end;
|
|
Result:=Result+')';
|
|
end
|
|
else if El.ClassType=TRecordValues then
|
|
begin
|
|
Result:=Result+'(';
|
|
l:=length(TRecordValues(El).Fields);
|
|
if l>0 then
|
|
begin
|
|
inc(Indent,2);
|
|
for i:=0 to l-1 do
|
|
begin
|
|
LineBreak(0);
|
|
Result:=Result+TRecordValues(El).Fields[i].Name+':'
|
|
+GetTreeDesc(TRecordValues(El).Fields[i].ValueExp,Indent);
|
|
if i<l-1 then
|
|
Result:=Result+','
|
|
end;
|
|
dec(Indent,2);
|
|
end;
|
|
Result:=Result+')';
|
|
end
|
|
else if El.ClassType=TArrayValues then
|
|
begin
|
|
Result:=Result+'[';
|
|
l:=length(TArrayValues(El).Values);
|
|
if l>0 then
|
|
begin
|
|
inc(Indent,2);
|
|
for i:=0 to l-1 do
|
|
begin
|
|
LineBreak(0);
|
|
Result:=Result+GetTreeDesc(TArrayValues(El).Values[i],Indent);
|
|
if i<l-1 then
|
|
Result:=Result+','
|
|
end;
|
|
dec(Indent,2);
|
|
end;
|
|
Result:=Result+']';
|
|
end;
|
|
end
|
|
else if El is TPasProcedure then
|
|
begin
|
|
Result:=Result+GetTreeDesc(TPasProcedure(El).ProcType,Indent);
|
|
end
|
|
else if El is TPasProcedureType then
|
|
begin
|
|
Result:=Result+'(';
|
|
l:=TPasProcedureType(El).Args.Count;
|
|
if l>0 then
|
|
begin
|
|
inc(Indent,2);
|
|
for i:=0 to l-1 do
|
|
begin
|
|
LineBreak(0);
|
|
Result:=Result+GetTreeDesc(TPasArgument(TPasProcedureType(El).Args[i]),Indent);
|
|
if i<l-1 then
|
|
Result:=Result+';'
|
|
end;
|
|
dec(Indent,2);
|
|
end;
|
|
Result:=Result+')';
|
|
if El is TPasFunction then
|
|
Result:=Result+':'+GetTreeDesc(TPasFunctionType(TPasFunction(El).ProcType).ResultEl,Indent);
|
|
if TPasProcedureType(El).IsOfObject then
|
|
Result:=Result+' '+ProcTypeModifiers[ptmOfObject];
|
|
if TPasProcedureType(El).IsNested then
|
|
Result:=Result+' '+ProcTypeModifiers[ptmIsNested];
|
|
if cCallingConventions[TPasProcedureType(El).CallingConvention]<>'' then
|
|
Result:=Result+'; '+cCallingConventions[TPasProcedureType(El).CallingConvention];
|
|
end
|
|
else if El.ClassType=TPasResultElement then
|
|
Result:=Result+GetTreeDesc(TPasResultElement(El).ResultType,Indent)
|
|
else if El.ClassType=TPasArgument then
|
|
begin
|
|
if AccessNames[TPasArgument(El).Access]<>'' then
|
|
Result:=Result+AccessNames[TPasArgument(El).Access];
|
|
if TPasArgument(El).ArgType=nil then
|
|
Result:=Result+'untyped'
|
|
else
|
|
Result:=Result+GetTreeDesc(TPasArgument(El).ArgType,Indent);
|
|
end
|
|
else if El.ClassType=TPasUnresolvedSymbolRef then
|
|
begin
|
|
if TPasUnresolvedSymbolRef(El).CustomData is TResElDataBuiltInProc then
|
|
Result:=Result+TResElDataBuiltInProc(TPasUnresolvedSymbolRef(El).CustomData).Signature;
|
|
end;
|
|
end;
|
|
|
|
function GetResolverResultDesc(const T: TPasResolverResult): string;
|
|
begin
|
|
if (T.BaseType=btCustom) and (T.TypeEl<>nil) and (T.TypeEl.Name<>'') then
|
|
Result:=T.TypeEl.Name
|
|
else
|
|
Result:=BaseTypeNames[T.BaseType];
|
|
if T.BaseType in [btSet,btRange,btArray] then
|
|
begin
|
|
if (T.SubType=btCustom) and (T.TypeEl<>nil) and (T.TypeEl.Name<>'') then
|
|
Result:=Result+' of '+GetTypeDesc(T.TypeEl,true)
|
|
else
|
|
Result:=Result+' of '+BaseTypeNames[T.SubType];
|
|
end;
|
|
if T.IdentEl<>nil then
|
|
begin
|
|
// named element
|
|
if T.IdentEl=T.TypeEl then
|
|
Result:=Result+',type '+GetTypeDesc(T.TypeEl,true)
|
|
else if T.IdentEl.Name<>'' then
|
|
Result:=Result+','+T.IdentEl.Name+'/'+T.IdentEl.ClassName+':'+GetTypeDesc(T.TypeEl,true)
|
|
else
|
|
Result:=Result+','+T.IdentEl.ElementTypeName+'/'+T.IdentEl.ClassName+':'+GetTypeDesc(T.TypeEl,true);
|
|
if T.ExprEl<>nil then
|
|
Result:=Result+'='+GetTreeDesc(T.ExprEl);
|
|
end
|
|
else if T.TypeEl<>nil then
|
|
begin
|
|
// anonymous constant expression with named type
|
|
Result:=Result+',const '+GetTreeDesc(T.TypeEl);
|
|
if T.ExprEl<>nil then
|
|
Result:=Result+'='+GetTreeDesc(T.ExprEl);
|
|
end
|
|
else
|
|
begin
|
|
// anonymous const expr without explicit type, e.g. 123.4
|
|
Result:=Result+',const '+GetTreeDesc(T.ExprEl);
|
|
end;
|
|
end;
|
|
|
|
function GetResolverResultDescription(const T: TPasResolverResult;
|
|
OnlyType: boolean): string;
|
|
|
|
function GetSubTypeName: string;
|
|
begin
|
|
if (T.TypeEl<>nil) and (T.TypeEl.Name<>'') then
|
|
Result:=T.TypeEl.Name
|
|
else
|
|
Result:=BaseTypeNames[T.SubType];
|
|
end;
|
|
|
|
var
|
|
ArrayEl: TPasArrayType;
|
|
begin
|
|
case T.BaseType of
|
|
btModule: exit(T.IdentEl.ElementTypeName+' '+T.IdentEl.Name);
|
|
btNil: exit('nil');
|
|
btRange:
|
|
Result:='range of '+GetSubTypeName;
|
|
btSet:
|
|
Result:='set literal of '+GetSubTypeName;
|
|
btArray:
|
|
Result:='array literal of '+GetSubTypeName;
|
|
btContext:
|
|
begin
|
|
if T.TypeEl.ClassType=TPasClassOfType then
|
|
Result:='class of '+TPasClassOfType(T.TypeEl).DestType.Name
|
|
else if T.TypeEl.ClassType=TPasAliasType then
|
|
Result:=TPasAliasType(T.TypeEl).DestType.Name
|
|
else if T.TypeEl.ClassType=TPasTypeAliasType then
|
|
Result:='type '+TPasAliasType(T.TypeEl).DestType.Name
|
|
else if T.TypeEl.ClassType=TPasArrayType then
|
|
begin
|
|
ArrayEl:=TPasArrayType(T.TypeEl);
|
|
if length(ArrayEl.Ranges)=0 then
|
|
Result:='array of '+ArrayEl.ElType.Name
|
|
else
|
|
Result:='static array[] of '+ArrayEl.ElType.Name;
|
|
end
|
|
else if T.TypeEl is TPasProcedureType then
|
|
Result:=GetProcDesc(TPasProcedureType(T.TypeEl),false)
|
|
else if T.TypeEl.Name<>'' then
|
|
Result:=T.TypeEl.Name
|
|
else
|
|
Result:=T.TypeEl.ElementTypeName;
|
|
end;
|
|
btCustom:
|
|
Result:=T.TypeEl.Name;
|
|
else
|
|
Result:=BaseTypeNames[T.BaseType];
|
|
end;
|
|
if (not OnlyType) and (T.TypeEl<>T.IdentEl) and (T.IdentEl<>nil) then
|
|
Result:=T.IdentEl.Name+':'+Result;
|
|
end;
|
|
|
|
function GetResolverResultDbg(const T: TPasResolverResult): string;
|
|
begin
|
|
Result:='bt='+BaseTypeNames[T.BaseType];
|
|
if T.SubType<>btNone then
|
|
Result:=Result+' Sub='+BaseTypeNames[T.SubType];
|
|
Result:=Result
|
|
+' Ident='+GetObjName(T.IdentEl)
|
|
+' Type='+GetObjName(T.TypeEl)
|
|
+' Expr='+GetObjName(T.ExprEl)
|
|
+' Flags='+ResolverResultFlagsToStr(T.Flags);
|
|
end;
|
|
|
|
function ResolverResultFlagsToStr(const Flags: TPasResolverResultFlags): string;
|
|
var
|
|
f: TPasResolverResultFlag;
|
|
s: string;
|
|
begin
|
|
Result:='';
|
|
for f in Flags do
|
|
begin
|
|
if Result<>'' then Result:=Result+',';
|
|
str(f,s);
|
|
Result:=Result+s;
|
|
end;
|
|
Result:='['+Result+']';
|
|
end;
|
|
|
|
procedure SetResolverIdentifier(out ResolvedType: TPasResolverResult;
|
|
BaseType: TResolverBaseType; IdentEl: TPasElement; TypeEl: TPasType;
|
|
Flags: TPasResolverResultFlags);
|
|
begin
|
|
ResolvedType.BaseType:=BaseType;
|
|
ResolvedType.SubType:=btNone;
|
|
ResolvedType.IdentEl:=IdentEl;
|
|
ResolvedType.TypeEl:=TypeEl;
|
|
ResolvedType.ExprEl:=nil;
|
|
ResolvedType.Flags:=Flags;
|
|
end;
|
|
|
|
procedure SetResolverTypeExpr(out ResolvedType: TPasResolverResult;
|
|
BaseType: TResolverBaseType; TypeEl: TPasType; Flags: TPasResolverResultFlags
|
|
);
|
|
begin
|
|
ResolvedType.BaseType:=BaseType;
|
|
ResolvedType.SubType:=btNone;
|
|
ResolvedType.IdentEl:=nil;
|
|
ResolvedType.TypeEl:=TypeEl;
|
|
ResolvedType.ExprEl:=nil;
|
|
ResolvedType.Flags:=Flags;
|
|
end;
|
|
|
|
procedure SetResolverValueExpr(out ResolvedType: TPasResolverResult;
|
|
BaseType: TResolverBaseType; TypeEl: TPasType; ExprEl: TPasExpr;
|
|
Flags: TPasResolverResultFlags);
|
|
begin
|
|
ResolvedType.BaseType:=BaseType;
|
|
ResolvedType.SubType:=btNone;
|
|
ResolvedType.IdentEl:=nil;
|
|
ResolvedType.TypeEl:=TypeEl;
|
|
ResolvedType.ExprEl:=ExprEl;
|
|
ResolvedType.Flags:=Flags;
|
|
end;
|
|
|
|
function ProcNeedsImplProc(Proc: TPasProcedure): boolean;
|
|
begin
|
|
Result:=true;
|
|
if Proc.IsExternal then exit(false);
|
|
if Proc.IsForward then exit;
|
|
if Proc.Parent.ClassType=TInterfaceSection then exit;
|
|
if Proc.Parent.ClassType=TPasClassType then
|
|
begin
|
|
// a method declaration
|
|
if not Proc.IsAbstract then exit;
|
|
end;
|
|
Result:=false;
|
|
end;
|
|
|
|
function dbgs(const Flags: TPasResolverComputeFlags): string;
|
|
var
|
|
s: string;
|
|
f: TPasResolverComputeFlag;
|
|
begin
|
|
Result:='';
|
|
for f in Flags do
|
|
if f in Flags then
|
|
begin
|
|
if Result<>'' then Result:=Result+',';
|
|
str(f,s);
|
|
Result:=Result+s;
|
|
end;
|
|
Result:='['+Result+']';
|
|
end;
|
|
|
|
function dbgs(const a: TResolvedRefAccess): string;
|
|
begin
|
|
str(a,Result);
|
|
end;
|
|
|
|
function dbgs(const Flags: TResolvedReferenceFlags): string;
|
|
var
|
|
s: string;
|
|
f: TResolvedReferenceFlag;
|
|
begin
|
|
Result:='';
|
|
for f in Flags do
|
|
if f in Flags then
|
|
begin
|
|
if Result<>'' then Result:=Result+',';
|
|
str(f,s);
|
|
Result:=Result+s;
|
|
end;
|
|
Result:='['+Result+']';
|
|
end;
|
|
|
|
{ TPasPropertyScope }
|
|
|
|
destructor TPasPropertyScope.Destroy;
|
|
begin
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasPropertyScope.Destroy START ',ClassName);
|
|
{$ENDIF}
|
|
ReleaseAndNil(TPasElement(AncestorProp));
|
|
inherited Destroy;
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasPropertyScope.Destroy END',ClassName);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ TPasEnumTypeScope }
|
|
|
|
destructor TPasEnumTypeScope.Destroy;
|
|
begin
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasEnumTypeScope.Destroy START ',ClassName);
|
|
{$ENDIF}
|
|
ReleaseAndNil(TPasElement(CanonicalSet));
|
|
inherited Destroy;
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasEnumTypeScope.Destroy END ',ClassName);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ TPasDotIdentifierScope }
|
|
|
|
function TPasDotIdentifierScope.FindIdentifier(const Identifier: String
|
|
): TPasIdentifier;
|
|
begin
|
|
Result:=IdentifierScope.FindIdentifier(Identifier);
|
|
end;
|
|
|
|
procedure TPasDotIdentifierScope.IterateElements(const aName: string;
|
|
StartScope: TPasScope; const OnIterateElement: TIterateScopeElement;
|
|
Data: Pointer; var Abort: boolean);
|
|
begin
|
|
IdentifierScope.IterateElements(aName, StartScope, OnIterateElement, Data, Abort);
|
|
end;
|
|
|
|
procedure TPasDotIdentifierScope.WriteIdentifiers(Prefix: string);
|
|
begin
|
|
IdentifierScope.WriteIdentifiers(Prefix);
|
|
end;
|
|
|
|
{ TPasWithExprScope }
|
|
|
|
class function TPasWithExprScope.IsStoredInElement: boolean;
|
|
begin
|
|
Result:=false;
|
|
end;
|
|
|
|
class function TPasWithExprScope.FreeOnPop: boolean;
|
|
begin
|
|
Result:=false;
|
|
end;
|
|
|
|
procedure TPasWithExprScope.IterateElements(const aName: string;
|
|
StartScope: TPasScope; const OnIterateElement: TIterateScopeElement;
|
|
Data: Pointer; var Abort: boolean);
|
|
begin
|
|
Scope.IterateElements(aName, StartScope, OnIterateElement, Data, Abort);
|
|
end;
|
|
|
|
procedure TPasWithExprScope.WriteIdentifiers(Prefix: string);
|
|
begin
|
|
writeln(Prefix+'WithExpr: '+GetTreeDesc(Expr,length(Prefix)));
|
|
Scope.WriteIdentifiers(Prefix);
|
|
end;
|
|
|
|
{ TPasWithScope }
|
|
|
|
constructor TPasWithScope.Create;
|
|
begin
|
|
inherited Create;
|
|
ExpressionScopes:=TObjectList.Create(true);
|
|
end;
|
|
|
|
destructor TPasWithScope.Destroy;
|
|
begin
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasWithScope.Destroy START ',ClassName);
|
|
{$ENDIF}
|
|
FreeAndNil(ExpressionScopes);
|
|
inherited Destroy;
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasWithScope.Destroy END ',ClassName);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ TPasProcedureScope }
|
|
|
|
function TPasProcedureScope.FindIdentifier(const Identifier: String
|
|
): TPasIdentifier;
|
|
begin
|
|
Result:=inherited FindIdentifier(Identifier);
|
|
if Result<>nil then exit;
|
|
if ClassScope<>nil then
|
|
Result:=ClassScope.FindIdentifier(Identifier);
|
|
end;
|
|
|
|
procedure TPasProcedureScope.IterateElements(const aName: string;
|
|
StartScope: TPasScope; const OnIterateElement: TIterateScopeElement;
|
|
Data: Pointer; var Abort: boolean);
|
|
begin
|
|
inherited IterateElements(aName, StartScope, OnIterateElement, Data, Abort);
|
|
if Abort then exit;
|
|
if ClassScope<>nil then
|
|
ClassScope.IterateElements(aName, StartScope, OnIterateElement, Data, Abort);
|
|
end;
|
|
|
|
procedure TPasProcedureScope.WriteIdentifiers(Prefix: string);
|
|
begin
|
|
inherited WriteIdentifiers(Prefix);
|
|
if ClassScope<>nil then
|
|
ClassScope.WriteIdentifiers(Prefix+' ');
|
|
end;
|
|
|
|
destructor TPasProcedureScope.Destroy;
|
|
begin
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasProcedureScope.Destroy START ',ClassName);
|
|
{$ENDIF}
|
|
inherited Destroy;
|
|
ReleaseAndNil(TPasElement(SelfArg));
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasProcedureScope.Destroy END ',ClassName);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ TPasClassScope }
|
|
|
|
function TPasClassScope.FindIdentifier(const Identifier: String
|
|
): TPasIdentifier;
|
|
begin
|
|
Result:=inherited FindIdentifier(Identifier);
|
|
if Result<>nil then exit;
|
|
if AncestorScope<>nil then
|
|
Result:=AncestorScope.FindIdentifier(Identifier);
|
|
end;
|
|
|
|
procedure TPasClassScope.IterateElements(const aName: string;
|
|
StartScope: TPasScope; const OnIterateElement: TIterateScopeElement;
|
|
Data: Pointer; var Abort: boolean);
|
|
begin
|
|
inherited IterateElements(aName, StartScope, OnIterateElement, Data, Abort);
|
|
if Abort then exit;
|
|
if AncestorScope<>nil then
|
|
AncestorScope.IterateElements(aName,StartScope,OnIterateElement,Data,Abort);
|
|
end;
|
|
|
|
procedure TPasClassScope.WriteIdentifiers(Prefix: string);
|
|
begin
|
|
inherited WriteIdentifiers(Prefix);
|
|
if AncestorScope<>nil then
|
|
AncestorScope.WriteIdentifiers(Prefix+' ');
|
|
end;
|
|
|
|
{ TPasDotClassScope }
|
|
|
|
procedure TPasDotClassScope.SetClassScope(AValue: TPasClassScope);
|
|
begin
|
|
if FClassScope=AValue then Exit;
|
|
FClassScope:=AValue;
|
|
IdentifierScope:=AValue;
|
|
end;
|
|
|
|
{ TPasIdentifier }
|
|
|
|
procedure TPasIdentifier.SetElement(AValue: TPasElement);
|
|
begin
|
|
if FElement=AValue then Exit;
|
|
if Element<>nil then
|
|
Element.Release;
|
|
FElement:=AValue;
|
|
if Element<>nil then
|
|
Element.AddRef;
|
|
end;
|
|
|
|
destructor TPasIdentifier.Destroy;
|
|
begin
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasIdentifier.Destroy START ',ClassName,' "',Identifier,'"');
|
|
{$ENDIF}
|
|
Element:=nil;
|
|
inherited Destroy;
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasIdentifier.Destroy END ',ClassName);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ EPasResolve }
|
|
|
|
procedure EPasResolve.SetPasElement(AValue: TPasElement);
|
|
begin
|
|
if FPasElement=AValue then Exit;
|
|
if PasElement<>nil then
|
|
PasElement.Release;
|
|
FPasElement:=AValue;
|
|
if PasElement<>nil then
|
|
PasElement.AddRef;
|
|
end;
|
|
|
|
destructor EPasResolve.Destroy;
|
|
begin
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('EPasResolve.Destroy START ',ClassName);
|
|
{$ENDIF}
|
|
PasElement:=nil;
|
|
inherited Destroy;
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('EPasResolve.Destroy END ',ClassName);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ TResolvedReference }
|
|
|
|
procedure TResolvedReference.SetDeclaration(AValue: TPasElement);
|
|
begin
|
|
if FDeclaration=AValue then Exit;
|
|
if Declaration<>nil then
|
|
Declaration.Release;
|
|
FDeclaration:=AValue;
|
|
if Declaration<>nil then
|
|
Declaration.AddRef;
|
|
end;
|
|
|
|
destructor TResolvedReference.Destroy;
|
|
begin
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TResolvedReference.Destroy START ',ClassName);
|
|
{$ENDIF}
|
|
Declaration:=nil;
|
|
FreeAndNil(Context);
|
|
inherited Destroy;
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TResolvedReference.Destroy END ',ClassName);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ TPasSubScope }
|
|
|
|
class function TPasSubScope.IsStoredInElement: boolean;
|
|
begin
|
|
Result:=false;
|
|
end;
|
|
|
|
{ TPasModuleDotScope }
|
|
|
|
procedure TPasModuleDotScope.OnInternalIterate(El: TPasElement; ElScope,
|
|
StartScope: TPasScope; Data: Pointer; var Abort: boolean);
|
|
var
|
|
FilterData: PPasIterateFilterData absolute Data;
|
|
begin
|
|
if El.ClassType=TPasModule then
|
|
exit; // skip used units
|
|
// call the original iterator
|
|
FilterData^.OnIterate(El,ElScope,StartScope,FilterData^.Data,Abort);
|
|
end;
|
|
|
|
procedure TPasModuleDotScope.SetModule(AValue: TPasModule);
|
|
begin
|
|
if FModule=AValue then Exit;
|
|
if Module<>nil then
|
|
Module.Release;
|
|
FModule:=AValue;
|
|
if Module<>nil then
|
|
Module.AddRef;
|
|
end;
|
|
|
|
destructor TPasModuleDotScope.Destroy;
|
|
begin
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasSubModuleScope.Destroy START ',ClassName);
|
|
{$ENDIF}
|
|
Module:=nil;
|
|
inherited Destroy;
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasSubModuleScope.Destroy END ',ClassName);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TPasModuleDotScope.FindIdentifier(const Identifier: String
|
|
): TPasIdentifier;
|
|
begin
|
|
if ImplementationScope<>nil then
|
|
begin
|
|
Result:=ImplementationScope.FindLocalIdentifier(Identifier);
|
|
if (Result<>nil) and (Result.Element.ClassType<>TPasModule) then
|
|
exit;
|
|
end;
|
|
if InterfaceScope<>nil then
|
|
Result:=InterfaceScope.FindLocalIdentifier(Identifier)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
procedure TPasModuleDotScope.IterateElements(const aName: string;
|
|
StartScope: TPasScope; const OnIterateElement: TIterateScopeElement;
|
|
Data: Pointer; var Abort: boolean);
|
|
var
|
|
FilterData: TPasIterateFilterData;
|
|
begin
|
|
FilterData.OnIterate:=OnIterateElement;
|
|
FilterData.Data:=Data;
|
|
if ImplementationScope<>nil then
|
|
begin
|
|
ImplementationScope.IterateElements(aName,StartScope,@OnInternalIterate,@FilterData,Abort);
|
|
if Abort then exit;
|
|
end;
|
|
if InterfaceScope<>nil then
|
|
InterfaceScope.IterateElements(aName,StartScope,@OnInternalIterate,@FilterData,Abort);
|
|
end;
|
|
|
|
procedure TPasModuleDotScope.WriteIdentifiers(Prefix: string);
|
|
begin
|
|
if ImplementationScope<>nil then
|
|
ImplementationScope.WriteIdentifiers(Prefix+' ');
|
|
if InterfaceScope<>nil then
|
|
InterfaceScope.WriteIdentifiers(Prefix+' ');
|
|
end;
|
|
|
|
{ TPasSectionScope }
|
|
|
|
constructor TPasSectionScope.Create;
|
|
begin
|
|
inherited Create;
|
|
UsesList:=TFPList.Create;
|
|
end;
|
|
|
|
destructor TPasSectionScope.Destroy;
|
|
begin
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasSectionScope.Destroy START ',ClassName);
|
|
{$ENDIF}
|
|
FreeAndNil(UsesList);
|
|
inherited Destroy;
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasSectionScope.Destroy END ',ClassName);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TPasSectionScope.FindIdentifier(const Identifier: String
|
|
): TPasIdentifier;
|
|
var
|
|
i: Integer;
|
|
UsesScope: TPasIdentifierScope;
|
|
begin
|
|
Result:=inherited FindIdentifier(Identifier);
|
|
if Result<>nil then
|
|
exit;
|
|
for i:=0 to UsesList.Count-1 do
|
|
begin
|
|
UsesScope:=TPasIdentifierScope(UsesList[i]);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasSectionScope.FindIdentifier "',Identifier,'" in used unit ',GetObjName(UsesScope.Element));
|
|
{$ENDIF}
|
|
Result:=UsesScope.FindLocalIdentifier(Identifier);
|
|
if Result<>nil then exit;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasSectionScope.IterateElements(const aName: string;
|
|
StartScope: TPasScope; const OnIterateElement: TIterateScopeElement;
|
|
Data: Pointer; var Abort: boolean);
|
|
var
|
|
i: Integer;
|
|
UsesScope: TPasIdentifierScope;
|
|
begin
|
|
inherited IterateElements(aName, StartScope, OnIterateElement, Data, Abort);
|
|
if Abort then exit;
|
|
for i:=0 to UsesList.Count-1 do
|
|
begin
|
|
UsesScope:=TPasIdentifierScope(UsesList[i]);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasSectionScope.IterateElements "',aName,'" in used unit ',GetObjName(UsesScope.Element));
|
|
{$ENDIF}
|
|
UsesScope.IterateLocalElements(aName,StartScope,OnIterateElement,Data,Abort);
|
|
if Abort then exit;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasSectionScope.WriteIdentifiers(Prefix: string);
|
|
var
|
|
i: Integer;
|
|
UsesScope: TPasIdentifierScope;
|
|
begin
|
|
inherited WriteIdentifiers(Prefix);
|
|
for i:=0 to UsesList.Count-1 do
|
|
begin
|
|
UsesScope:=TPasIdentifierScope(UsesList[i]);
|
|
writeln(Prefix+'Uses: '+GetObjName(UsesScope.Element));
|
|
end;
|
|
end;
|
|
|
|
{ TPasModuleScope }
|
|
|
|
procedure TPasModuleScope.IterateElements(const aName: string;
|
|
StartScope: TPasScope; const OnIterateElement: TIterateScopeElement;
|
|
Data: Pointer; var Abort: boolean);
|
|
begin
|
|
if CompareText(aName,Element.Name)<>0 then exit;
|
|
OnIterateElement(Element,Self,StartScope,Data,Abort);
|
|
end;
|
|
|
|
{ TPasDefaultScope }
|
|
|
|
class function TPasDefaultScope.IsStoredInElement: boolean;
|
|
begin
|
|
Result:=false;
|
|
end;
|
|
|
|
{ TResolveData }
|
|
|
|
procedure TResolveData.SetElement(AValue: TPasElement);
|
|
begin
|
|
if FElement=AValue then Exit;
|
|
if Element<>nil then
|
|
Element.Release;
|
|
FElement:=AValue;
|
|
if Element<>nil then
|
|
Element.AddRef;
|
|
end;
|
|
|
|
constructor TResolveData.Create;
|
|
begin
|
|
|
|
end;
|
|
|
|
destructor TResolveData.Destroy;
|
|
begin
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TResolveData.Destroy START ',ClassName);
|
|
{$ENDIF}
|
|
Element:=nil;
|
|
Owner:=nil;
|
|
Next:=nil;
|
|
inherited Destroy;
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TResolveData.Destroy END ',ClassName);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ TPasScope }
|
|
|
|
class function TPasScope.IsStoredInElement: boolean;
|
|
begin
|
|
Result:=true;
|
|
end;
|
|
|
|
class function TPasScope.FreeOnPop: boolean;
|
|
begin
|
|
Result:=not IsStoredInElement;
|
|
end;
|
|
|
|
procedure TPasScope.IterateElements(const aName: string; StartScope: TPasScope;
|
|
const OnIterateElement: TIterateScopeElement; Data: Pointer;
|
|
var Abort: boolean);
|
|
begin
|
|
if aName='' then ;
|
|
if StartScope=nil then ;
|
|
if Data=nil then ;
|
|
if OnIterateElement=nil then ;
|
|
if Abort then ;
|
|
end;
|
|
|
|
procedure TPasScope.WriteIdentifiers(Prefix: string);
|
|
begin
|
|
writeln(Prefix,'Element: ',GetObjName(Element));
|
|
end;
|
|
|
|
{ TPasIdentifierScope }
|
|
|
|
// inline
|
|
function TPasIdentifierScope.FindLocalIdentifier(const Identifier: String
|
|
): TPasIdentifier;
|
|
var
|
|
LoName: String;
|
|
begin
|
|
LoName:=lowercase(Identifier);
|
|
Result:=TPasIdentifier(FItems.Find(LoName));
|
|
end;
|
|
|
|
procedure TPasIdentifierScope.OnClearItem(Item, Dummy: pointer);
|
|
var
|
|
PasIdentifier: TPasIdentifier absolute Item;
|
|
Ident: TPasIdentifier;
|
|
begin
|
|
if Dummy=nil then ;
|
|
//writeln('TPasIdentifierScope.OnClearItem ',PasIdentifier.Identifier+':'+PasIdentifier.ClassName);
|
|
while PasIdentifier<>nil do
|
|
begin
|
|
Ident:=PasIdentifier;
|
|
PasIdentifier:=PasIdentifier.NextSameIdentifier;
|
|
Ident.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasIdentifierScope.OnWriteItem(Item, Dummy: pointer);
|
|
var
|
|
PasIdentifier: TPasIdentifier absolute Item;
|
|
Prefix: String;
|
|
begin
|
|
Prefix:=AnsiString(Dummy);
|
|
while PasIdentifier<>nil do
|
|
begin
|
|
writeln(Prefix,'Identifier="',PasIdentifier.Identifier,'" Element=',GetObjName(PasIdentifier.Element));
|
|
PasIdentifier:=PasIdentifier.NextSameIdentifier;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasIdentifierScope.InternalAdd(Item: TPasIdentifier);
|
|
var
|
|
Index: Integer;
|
|
OldItem: TPasIdentifier;
|
|
LoName: ShortString;
|
|
begin
|
|
LoName:=lowercase(Item.Identifier);
|
|
Index:=FItems.FindIndexOf(LoName);
|
|
{$IFDEF VerbosePasResolver}
|
|
if Item.Owner<>nil then
|
|
raise Exception.Create('20160925184110');
|
|
Item.Owner:=Self;
|
|
{$ENDIF}
|
|
//writeln(' Index=',Index);
|
|
if Index>=0 then
|
|
begin
|
|
// insert LIFO - last in, first out
|
|
OldItem:=TPasIdentifier(FItems.List^[Index].Data);
|
|
{$IFDEF VerbosePasResolver}
|
|
if lowercase(OldItem.Identifier)<>LoName then
|
|
raise Exception.Create('20160925183438');
|
|
{$ENDIF}
|
|
Item.NextSameIdentifier:=OldItem;
|
|
FItems.List^[Index].Data:=Item;
|
|
end
|
|
else
|
|
begin
|
|
FItems.Add(LoName, Item);
|
|
{$IFDEF VerbosePasResolver}
|
|
if FindIdentifier(Item.Identifier)<>Item then
|
|
raise Exception.Create('20160925183849');
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
constructor TPasIdentifierScope.Create;
|
|
begin
|
|
FItems:=TFPHashList.Create;
|
|
end;
|
|
|
|
destructor TPasIdentifierScope.Destroy;
|
|
begin
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasIdentifierScope.Destroy START ',ClassName);
|
|
{$ENDIF}
|
|
FItems.ForEachCall(@OnClearItem,nil);
|
|
FItems.Clear;
|
|
FreeAndNil(FItems);
|
|
inherited Destroy;
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasIdentifierScope.Destroy END ',ClassName);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TPasIdentifierScope.FindIdentifier(const Identifier: String
|
|
): TPasIdentifier;
|
|
begin
|
|
Result:=FindLocalIdentifier(Identifier);
|
|
{$IFDEF VerbosePasResolver}
|
|
if (Result<>nil) and (Result.Owner<>Self) then
|
|
begin
|
|
writeln('TPasIdentifierScope.FindIdentifier Result.Owner<>Self Owner='+GetObjName(Result.Owner));
|
|
raise Exception.Create('20160925184159');
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TPasIdentifierScope.RemoveLocalIdentifier(El: TPasElement): boolean;
|
|
var
|
|
Identifier, PrevIdentifier: TPasIdentifier;
|
|
LoName: ShortString;
|
|
begin
|
|
LoName:=lowercase(El.Name);
|
|
Identifier:=TPasIdentifier(FItems.Find(LoName));
|
|
FindLocalIdentifier(El.Name);
|
|
PrevIdentifier:=nil;
|
|
Result:=false;
|
|
while Identifier<>nil do
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
if (Identifier.Owner<>Self) then
|
|
raise Exception.Create('20160925184159');
|
|
{$ENDIF}
|
|
if Identifier.Element=El then
|
|
begin
|
|
if PrevIdentifier<>nil then
|
|
begin
|
|
PrevIdentifier.NextSameIdentifier:=Identifier.NextSameIdentifier;
|
|
Identifier.Free;
|
|
Identifier:=PrevIdentifier.NextSameIdentifier;
|
|
end
|
|
else
|
|
begin
|
|
FItems.Remove(Identifier);
|
|
PrevIdentifier:=Identifier;
|
|
Identifier:=Identifier.NextSameIdentifier;
|
|
PrevIdentifier.Free;
|
|
PrevIdentifier:=nil;
|
|
if Identifier<>nil then
|
|
FItems.Add(Loname,Identifier);
|
|
end;
|
|
Result:=true;
|
|
continue;
|
|
end;
|
|
PrevIdentifier:=Identifier;
|
|
Identifier:=Identifier.NextSameIdentifier;
|
|
end;
|
|
end;
|
|
|
|
function TPasIdentifierScope.AddIdentifier(const Identifier: String;
|
|
El: TPasElement; const Kind: TPasIdentifierKind): TPasIdentifier;
|
|
var
|
|
Item: TPasIdentifier;
|
|
begin
|
|
//writeln('TPasIdentifierScope.AddIdentifier Identifier="',Identifier,'" El=',GetObjName(El));
|
|
Item:=TPasIdentifier.Create;
|
|
Item.Identifier:=Identifier;
|
|
Item.Element:=El;
|
|
Item.Kind:=Kind;
|
|
|
|
InternalAdd(Item);
|
|
//writeln('TPasIdentifierScope.AddIdentifier END');
|
|
Result:=Item;
|
|
end;
|
|
|
|
function TPasIdentifierScope.FindElement(const aName: string): TPasElement;
|
|
var
|
|
Item: TPasIdentifier;
|
|
begin
|
|
//writeln('TPasIdentifierScope.FindElement "',aName,'"');
|
|
Item:=FindIdentifier(aName);
|
|
if Item=nil then
|
|
Result:=nil
|
|
else
|
|
Result:=Item.Element;
|
|
//writeln('TPasIdentifierScope.FindElement Found="',GetObjName(Result),'"');
|
|
end;
|
|
|
|
procedure TPasIdentifierScope.IterateLocalElements(const aName: string;
|
|
StartScope: TPasScope; const OnIterateElement: TIterateScopeElement;
|
|
Data: Pointer; var Abort: boolean);
|
|
var
|
|
Item: TPasIdentifier;
|
|
{$IFDEF VerbosePasResolver}
|
|
OldElement: TPasElement;
|
|
{$ENDIF}
|
|
begin
|
|
Item:=FindLocalIdentifier(aName);
|
|
while Item<>nil do
|
|
begin
|
|
//writeln('TPasIdentifierScope.IterateLocalElements ',ClassName,' ',Item.Identifier,' ',GetObjName(Item.Element));
|
|
{$IFDEF VerbosePasResolver}
|
|
OldElement:=Item.Element;
|
|
{$ENDIF}
|
|
OnIterateElement(Item.Element,Self,StartScope,Data,Abort);
|
|
{$IFDEF VerbosePasResolver}
|
|
if OldElement<>Item.Element then
|
|
raise Exception.Create('20160925183503');
|
|
{$ENDIF}
|
|
if Abort then exit;
|
|
Item:=Item.NextSameIdentifier;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasIdentifierScope.IterateElements(const aName: string;
|
|
StartScope: TPasScope; const OnIterateElement: TIterateScopeElement;
|
|
Data: Pointer; var Abort: boolean);
|
|
begin
|
|
IterateLocalElements(aName,StartScope,OnIterateElement,Data,Abort);
|
|
end;
|
|
|
|
procedure TPasIdentifierScope.WriteIdentifiers(Prefix: string);
|
|
begin
|
|
inherited WriteIdentifiers(Prefix);
|
|
Prefix:=Prefix+' ';
|
|
FItems.ForEachCall(@OnWriteItem,Pointer(Prefix));
|
|
end;
|
|
|
|
{ TPasResolver }
|
|
|
|
// inline
|
|
function TPasResolver.GetBaseTypes(bt: TResolverBaseType
|
|
): TPasUnresolvedSymbolRef;
|
|
begin
|
|
Result:=FBaseTypes[bt];
|
|
end;
|
|
|
|
// inline
|
|
function TPasResolver.GetScopes(Index: integer): TPasScope;
|
|
begin
|
|
Result:=FScopes[Index];
|
|
end;
|
|
|
|
// inline
|
|
function TPasResolver.IsNameExpr(El: TPasExpr): boolean;
|
|
begin
|
|
if El.ClassType=TSelfExpr then exit(true);
|
|
Result:=(El.ClassType=TPrimitiveExpr) and (TPrimitiveExpr(El).Kind=pekIdent);
|
|
end;
|
|
|
|
procedure TPasResolver.ClearResolveDataList(Kind: TResolveDataListKind);
|
|
var
|
|
El: TPasElement;
|
|
RData: TResolveData;
|
|
begin
|
|
// clear CustomData
|
|
while FLastCreatedData[Kind]<>nil do
|
|
begin
|
|
RData:=FLastCreatedData[Kind];
|
|
El:=RData.Element;
|
|
El.CustomData:=nil;
|
|
FLastCreatedData[Kind]:=RData.Next;
|
|
RData.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.OnFindFirstElement(El: TPasElement; ElScope,
|
|
StartScope: TPasScope; FindFirstElementData: Pointer; var Abort: boolean);
|
|
var
|
|
Data: PPRFindData absolute FindFirstElementData;
|
|
ok: Boolean;
|
|
begin
|
|
ok:=true;
|
|
if (El is TPasProcedure)
|
|
and ProcNeedsParams(TPasProcedure(El).ProcType) then
|
|
// found a proc, but it needs parameters -> remember the first and continue
|
|
ok:=false;
|
|
if ok or (Data^.Found=nil) then
|
|
begin
|
|
Data^.Found:=El;
|
|
Data^.ElScope:=ElScope;
|
|
Data^.StartScope:=StartScope;
|
|
end;
|
|
if ok then
|
|
Abort:=true;
|
|
end;
|
|
|
|
procedure TPasResolver.OnFindCallElements(El: TPasElement; ElScope,
|
|
StartScope: TPasScope; FindProcsData: Pointer; var Abort: boolean);
|
|
var
|
|
Data: PFindCallElData absolute FindProcsData;
|
|
Proc, PrevProc: TPasProcedure;
|
|
Distance: integer;
|
|
BuiltInProc: TResElDataBuiltInProc;
|
|
CandidateFound: Boolean;
|
|
VarType, TypeEl: TPasType;
|
|
C: TClass;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnFindCallElements START ---------');
|
|
{$ENDIF}
|
|
CandidateFound:=false;
|
|
|
|
if (El is TPasProcedure) then
|
|
begin
|
|
// identifier is a proc
|
|
Proc:=TPasProcedure(El);
|
|
|
|
if Data^.Found=Proc then
|
|
begin
|
|
// this proc was already found. This happens when this is the forward
|
|
// declaration or a previously found implementation.
|
|
Data^.ElScope:=ElScope;
|
|
Data^.StartScope:=StartScope;
|
|
exit;
|
|
end;
|
|
|
|
if (Proc.CustomData is TPasProcedureScope)
|
|
and (TPasProcedureScope(Proc.CustomData).DeclarationProc<>nil)
|
|
then
|
|
begin
|
|
// this proc has a forward declaration -> use that instead
|
|
Proc:=TPasProcedureScope(Proc.CustomData).DeclarationProc;
|
|
El:=Proc;
|
|
end;
|
|
|
|
if Data^.Found is TPasProcedure then
|
|
begin
|
|
// there is already a previous proc
|
|
PrevProc:=TPasProcedure(Data^.Found);
|
|
|
|
if (Data^.Distance=cExact) and (PrevProc.Parent<>Proc.Parent)
|
|
and (PrevProc.Parent.ClassType=TPasClassType) then
|
|
begin
|
|
// there was already a perfect proc in a descendant
|
|
Abort:=true;
|
|
exit;
|
|
end;
|
|
|
|
// check if previous found proc is override of found proc
|
|
if (PrevProc.IsOverride)
|
|
and (TPasProcedureScope(PrevProc.CustomData).OverriddenProc=Proc) then
|
|
begin
|
|
// previous found proc is override of found proc -> skip
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
Distance:=CheckCallProcCompatibility(Proc.ProcType,Data^.Params,false);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnFindCallElements Proc Compatible=',Distance,' Data^.Found=',Data^.Found<>nil,' Data^.Compatible=',ord(Data^.Distance));
|
|
{$ENDIF}
|
|
CandidateFound:=true;
|
|
end
|
|
else if El is TPasType then
|
|
begin
|
|
TypeEl:=ResolveAliasType(TPasType(El));
|
|
C:=TypeEl.ClassType;
|
|
if C=TPasUnresolvedSymbolRef then
|
|
begin
|
|
if TypeEl.CustomData.ClassType=TResElDataBuiltInProc then
|
|
begin
|
|
// call of built-in proc
|
|
BuiltInProc:=TResElDataBuiltInProc(TypeEl.CustomData);
|
|
if (BuiltInProc.BuiltIn in [bfStrProc,bfStrFunc])
|
|
and ((BuiltInProc.BuiltIn=bfStrProc) = ParentNeedsExprResult(Data^.Params)) then
|
|
begin
|
|
// str function can only be used within an expression
|
|
// str procedure can only be used outside an expression
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnFindCallElements BuiltInProc=',El.Name,' skip');
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
Distance:=BuiltInProc.GetCallCompatibility(BuiltInProc,Data^.Params,false);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnFindCallElements BuiltInProc=',El.Name,' Distance=',Distance);
|
|
{$ENDIF}
|
|
CandidateFound:=true;
|
|
end
|
|
else if TypeEl.CustomData is TResElDataBaseType then
|
|
begin
|
|
// type cast to base type
|
|
Abort:=true; // can't be overloaded
|
|
if Data^.Found<>nil then exit;
|
|
Distance:=CheckTypeCast(TPasType(El),Data^.Params,false);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnFindCallElements Base type cast=',El.Name,' Distance=',Distance);
|
|
{$ENDIF}
|
|
CandidateFound:=true;
|
|
end;
|
|
end
|
|
else if (C=TPasClassType)
|
|
or (C=TPasClassOfType)
|
|
or (C=TPasEnumType)
|
|
or (C=TPasProcedureType)
|
|
or (C=TPasFunctionType)
|
|
or (C=TPasArrayType) then
|
|
begin
|
|
// type cast to user type
|
|
Abort:=true; // can't be overloaded
|
|
if Data^.Found<>nil then exit;
|
|
Distance:=CheckTypeCast(TPasType(El),Data^.Params,false);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnFindCallElements type cast to =',GetObjName(El),' Distance=',Distance);
|
|
{$ENDIF}
|
|
CandidateFound:=true;
|
|
end;
|
|
end
|
|
else if El is TPasVariable then
|
|
begin
|
|
Abort:=true; // can't be overloaded
|
|
if Data^.Found<>nil then exit;
|
|
VarType:=ResolveAliasType(TPasVariable(El).VarType);
|
|
if VarType is TPasProcedureType then
|
|
begin
|
|
Distance:=CheckCallProcCompatibility(TPasProcedureType(VarType),Data^.Params,false);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnFindCallElements call var of proctype=',El.Name,' Distance=',Distance);
|
|
{$ENDIF}
|
|
CandidateFound:=true;
|
|
end;
|
|
end
|
|
else if El.ClassType=TPasArgument then
|
|
begin
|
|
Abort:=true; // can't be overloaded
|
|
if Data^.Found<>nil then exit;
|
|
VarType:=ResolveAliasType(TPasArgument(El).ArgType);
|
|
if VarType is TPasProcedureType then
|
|
begin
|
|
Distance:=CheckCallProcCompatibility(TPasProcedureType(VarType),Data^.Params,false);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnFindCallElements call arg of proctype=',El.Name,' Distance=',Distance);
|
|
{$ENDIF}
|
|
CandidateFound:=true;
|
|
end;
|
|
end;
|
|
|
|
if not CandidateFound then
|
|
begin
|
|
// El does not support the () operator
|
|
Abort:=true;
|
|
if Data^.Found=nil then
|
|
begin
|
|
// El is the first element found -> raise error
|
|
// ToDo: use the ( as error position
|
|
RaiseMsg(20170216151525,nIllegalQualifier,sIllegalQualifier,['('],Data^.Params);
|
|
end;
|
|
exit;
|
|
end;
|
|
|
|
// El is a candidate
|
|
if (Data^.Found=nil) or (Distance<Data^.Distance) then
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnFindCallElements Found a better candidate Distance=',Distance,' Data^.Distance=',Data^.Distance);
|
|
{$ENDIF}
|
|
Data^.Found:=El;
|
|
Data^.ElScope:=ElScope;
|
|
Data^.StartScope:=StartScope;
|
|
Data^.Distance:=Distance;
|
|
Data^.Count:=1;
|
|
if Data^.List<>nil then
|
|
begin
|
|
Data^.List.Clear;
|
|
Data^.List.Add(El);
|
|
end;
|
|
end
|
|
else if Distance=Data^.Distance then
|
|
begin
|
|
inc(Data^.Count);
|
|
if (Data^.List<>nil) then
|
|
begin
|
|
if (Data^.List.IndexOf(El)>=0) then
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnFindCallElements Found El twice: ',GetTreeDesc(El),
|
|
' ',GetElementSourcePosStr(El),
|
|
' PrevElScope=',GetObjName(Data^.ElScope),' ',GetTreeDesc(Data^.ElScope.Element),
|
|
' ElScope=',GetObjName(ElScope),' ',GetTreeDesc(ElScope.Element)
|
|
);
|
|
{$ENDIF}
|
|
RaiseInternalError(20160924230805);
|
|
end;
|
|
Data^.List.Add(El);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.OnFindOverloadProc(El: TPasElement; ElScope,
|
|
StartScope: TPasScope; FindOverloadData: Pointer; var Abort: boolean);
|
|
var
|
|
Data: PFindOverloadProcData absolute FindOverloadData;
|
|
Proc: TPasProcedure;
|
|
begin
|
|
//writeln('TPasResolver.OnFindOverloadProc START ',El.Name,':',El.ElementTypeName,' itself=',El=Data^.Proc);
|
|
if not (El is TPasProcedure) then
|
|
begin
|
|
// identifier is not a proc
|
|
if (El is TPasVariable) then
|
|
begin
|
|
if TPasVariable(El).Visibility=visStrictPrivate then
|
|
exit;
|
|
if (TPasVariable(El).Visibility=visPrivate)
|
|
and (El.GetModule<>StartScope.Element.GetModule) then
|
|
exit;
|
|
end;
|
|
Data^.FoundNonProc:=El;
|
|
Abort:=true;
|
|
exit;
|
|
end;
|
|
// identifier is a proc
|
|
if El=Data^.Proc then
|
|
exit; // found itself -> normal when searching for overloads
|
|
|
|
//writeln('TPasResolver.OnFindOverloadProc Data^.OnlyScope=',GetObjName(Data^.OnlyScope),' ElScope=',GetObjName(ElScope),' ',Data^.OnlyScope=ElScope);
|
|
if (Data^.OnlyScope<>nil) and (Data^.OnlyScope<>ElScope) then
|
|
begin
|
|
// do not search any further, only one scope should be searched
|
|
// for example when searching the method declaration of a method body
|
|
Abort:=false;
|
|
exit;
|
|
end;
|
|
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnFindOverloadProc ',GetTreeDesc(El,2));
|
|
{$ENDIF}
|
|
Proc:=TPasProcedure(El);
|
|
if CheckOverloadProcCompatibility(Data^.Proc,Proc) then
|
|
begin
|
|
Data^.Found:=Proc;
|
|
Data^.ElScope:=ElScope;
|
|
Data^.StartScope:=StartScope;
|
|
Abort:=true;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.SetCurrentParser(AValue: TPasParser);
|
|
begin
|
|
//writeln('TPasResolver.SetCurrentParser ',AValue<>nil);
|
|
if AValue=CurrentParser then exit;
|
|
Clear;
|
|
inherited SetCurrentParser(AValue);
|
|
if CurrentParser<>nil then
|
|
CurrentParser.Options:=CurrentParser.Options
|
|
+[po_resolvestandardtypes,po_nooverloadedprocs,po_keepclassforward,
|
|
po_arrayrangeexpr];
|
|
end;
|
|
|
|
procedure TPasResolver.CheckTopScope(ExpectedClass: TPasScopeClass);
|
|
begin
|
|
if TopScope=nil then
|
|
RaiseInternalError(20160922163319,'Expected TopScope='+ExpectedClass.ClassName+' but found nil');
|
|
if TopScope.ClassType<>ExpectedClass then
|
|
RaiseInternalError(20160922163323,'Expected TopScope='+ExpectedClass.ClassName+' but found '+TopScope.ClassName);
|
|
end;
|
|
|
|
function TPasResolver.AddIdentifier(Scope: TPasIdentifierScope;
|
|
const aName: String; El: TPasElement; const Kind: TPasIdentifierKind
|
|
): TPasIdentifier;
|
|
var
|
|
Identifier, OlderIdentifier: TPasIdentifier;
|
|
ClassScope: TPasClassScope;
|
|
OlderEl: TPasElement;
|
|
IsClassScope: Boolean;
|
|
C: TClass;
|
|
begin
|
|
IsClassScope:=(Scope is TPasClassScope);
|
|
|
|
if (El.Visibility=visPublished) then
|
|
begin
|
|
C:=El.ClassType;
|
|
if (C=TPasProperty) or (C=TPasVariable) then
|
|
// Note: VarModifiers are not yet set
|
|
else if (C=TPasProcedure) or (C=TPasFunction) then
|
|
// ok
|
|
else
|
|
RaiseMsg(20170403223024,nSymbolCannotBePublished,sSymbolCannotBePublished,[],El);
|
|
end;
|
|
|
|
if (Kind=pikSimple) and IsClassScope
|
|
and (El.ClassType<>TPasProperty) then
|
|
begin
|
|
// check duplicate in ancestors
|
|
ClassScope:=TPasClassScope(Scope).AncestorScope;
|
|
while ClassScope<>nil do
|
|
begin
|
|
OlderIdentifier:=ClassScope.FindLocalIdentifier(aName);
|
|
while OlderIdentifier<>nil do
|
|
begin
|
|
OlderEl:=OlderIdentifier.Element;
|
|
OlderIdentifier:=OlderIdentifier.NextSameIdentifier;
|
|
if OlderEl is TPasVariable then
|
|
begin
|
|
if TPasVariable(OlderEl).Visibility=visStrictPrivate then
|
|
continue; // OlderEl is hidden
|
|
if (TPasVariable(OlderEl).Visibility=visPrivate)
|
|
and (OlderEl.GetModule<>El.GetModule) then
|
|
continue; // OlderEl is hidden
|
|
end;
|
|
RaiseMsg(20170221130001,nDuplicateIdentifier,sDuplicateIdentifier,
|
|
[aName,GetElementSourcePosStr(OlderEl)],El);
|
|
end;
|
|
ClassScope:=ClassScope.AncestorScope;
|
|
end;
|
|
end;
|
|
|
|
Identifier:=Scope.AddIdentifier(aName,El,Kind);
|
|
|
|
// check duplicate in current scope
|
|
OlderIdentifier:=Identifier.NextSameIdentifier;
|
|
if (OlderIdentifier<>nil) then
|
|
if (Identifier.Kind=pikSimple)
|
|
or (OlderIdentifier.Kind=pikSimple)
|
|
or (El.Visibility=visPublished) then
|
|
begin
|
|
if (OlderIdentifier.Element.ClassType=TPasEnumValue)
|
|
and (OlderIdentifier.Element.Parent.Parent<>Scope.Element) then
|
|
// this enum was propagated from a sub type -> remove enum
|
|
Scope.RemoveLocalIdentifier(OlderIdentifier.Element);
|
|
RaiseMsg(20170216151530,nDuplicateIdentifier,sDuplicateIdentifier,
|
|
[aName,GetElementSourcePosStr(OlderIdentifier.Element)],El);
|
|
end;
|
|
|
|
Result:=Identifier;
|
|
end;
|
|
|
|
procedure TPasResolver.FinishModule(CurModule: TPasModule);
|
|
var
|
|
CurModuleClass: TClass;
|
|
i: Integer;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.FinishModule START ',CurModule.Name);
|
|
{$ENDIF}
|
|
CurModuleClass:=CurModule.ClassType;
|
|
if (CurModuleClass=TPasProgram) or (CurModuleClass=TPasLibrary) then
|
|
begin
|
|
// resolve begin..end block
|
|
ResolveImplBlock(CurModule.InitializationSection);
|
|
end
|
|
else if (CurModuleClass=TPasModule) then
|
|
begin
|
|
if CurModule.FinalizationSection<>nil then
|
|
// finalization section finished -> resolve
|
|
ResolveImplBlock(CurModule.FinalizationSection);
|
|
if CurModule.InitializationSection<>nil then
|
|
// initialization section finished -> resolve
|
|
ResolveImplBlock(CurModule.InitializationSection);
|
|
end
|
|
else
|
|
RaiseInternalError(20160922163327); // unknown module
|
|
|
|
// check all methods have bodies
|
|
// and all forward classes and pointers are resolved
|
|
for i:=0 to FPendingForwards.Count-1 do
|
|
CheckPendingForwards(TPasElement(FPendingForwards[i]));
|
|
FPendingForwards.Clear;
|
|
|
|
// close all sections
|
|
while (TopScope<>nil) and (TopScope.ClassType=TPasSectionScope) do
|
|
PopScope;
|
|
CheckTopScope(TPasModuleScope);
|
|
PopScope;
|
|
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.FinishModule END ',CurModule.Name);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TPasResolver.FinishUsesList;
|
|
var
|
|
Section: TPasSection;
|
|
i: Integer;
|
|
El, PublicEl: TPasElement;
|
|
Scope: TPasSectionScope;
|
|
UsesScope: TPasIdentifierScope;
|
|
begin
|
|
CheckTopScope(TPasSectionScope);
|
|
Scope:=TPasSectionScope(TopScope);
|
|
Section:=TPasSection(Scope.Element);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.FinishUsesList Section=',Section.ClassName,' Section.UsesList.Count=',Section.UsesList.Count);
|
|
{$ENDIF}
|
|
for i:=0 to Section.UsesList.Count-1 do
|
|
begin
|
|
El:=TPasElement(Section.UsesList[i]);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.FinishUsesList ',GetObjName(El));
|
|
{$ENDIF}
|
|
if (El.ClassType=TProgramSection) then
|
|
RaiseInternalError(20160922163346,'used unit is a program: '+GetObjName(El));
|
|
|
|
// add unitname as identifier
|
|
AddIdentifier(Scope,El.Name,El,pikSimple);
|
|
|
|
// check used unit
|
|
PublicEl:=nil;
|
|
if (El.ClassType=TLibrarySection) then
|
|
PublicEl:=El
|
|
else if (El.ClassType=TPasModule) then
|
|
PublicEl:=TPasModule(El).InterfaceSection;
|
|
if PublicEl=nil then
|
|
RaiseInternalError(20160922163352,'uses element has no interface section: '+GetObjName(El));
|
|
if PublicEl.CustomData=nil then
|
|
RaiseInternalError(20160922163358,'uses element has no resolver data: '
|
|
+El.Name+'->'+GetObjName(PublicEl));
|
|
if not (PublicEl.CustomData is TPasIdentifierScope) then
|
|
RaiseInternalError(20160922163403,'uses element has invalid resolver data: '
|
|
+El.Name+'->'+GetObjName(PublicEl)+'->'+PublicEl.CustomData.ClassName);
|
|
|
|
UsesScope:=TPasIdentifierScope(PublicEl.CustomData);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.FinishUsesList Add UsesScope=',GetObjName(UsesScope));
|
|
{$ENDIF}
|
|
Scope.UsesList.Add(UsesScope);
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.FinishTypeSection(El: TPasDeclarations);
|
|
var
|
|
i: Integer;
|
|
Decl: TPasElement;
|
|
ClassOfEl: TPasClassOfType;
|
|
Data: TPRFindData;
|
|
UnresolvedEl: TUnresolvedPendingRef;
|
|
Abort: boolean;
|
|
OldClassType: TPasClassType;
|
|
ClassOfName: String;
|
|
begin
|
|
// resolve pending forwards
|
|
for i:=0 to El.Declarations.Count-1 do
|
|
begin
|
|
Decl:=TPasElement(El.Declarations[i]);
|
|
if Decl is TPasClassType then
|
|
begin
|
|
if TPasClassType(Decl).IsForward and (TPasClassType(Decl).CustomData=nil) then
|
|
RaiseMsg(20170216151534,nForwardTypeNotResolved,sForwardTypeNotResolved,[Decl.Name],Decl);
|
|
end
|
|
else if (Decl.ClassType=TPasClassOfType) then
|
|
begin
|
|
ClassOfEl:=TPasClassOfType(Decl);
|
|
Data:=Default(TPRFindData);
|
|
if (ClassOfEl.DestType.ClassType=TUnresolvedPendingRef) then
|
|
begin
|
|
// forward class-of -> resolve now
|
|
UnresolvedEl:=TUnresolvedPendingRef(ClassOfEl.DestType);
|
|
ClassOfName:=UnresolvedEl.Name;
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.FinishTypeSection resolving "',ClassOfEl.Name,'" = class of unresolved "',ClassOfName,'"');
|
|
{$ENDIF}
|
|
Data.ErrorPosEl:=UnresolvedEl;
|
|
Abort:=false;
|
|
(TopScope as TPasIdentifierScope).IterateElements(ClassOfName,
|
|
TopScope,@OnFindFirstElement,@Data,Abort);
|
|
if (Data.Found=nil) then
|
|
RaiseIdentifierNotFound(20170216151543,UnresolvedEl.Name,UnresolvedEl);
|
|
if Data.Found.ClassType<>TPasClassType then
|
|
RaiseXExpectedButYFound(20170216151548,'class',Data.Found.ElementTypeName,UnresolvedEl);
|
|
// replace unresolved
|
|
ClassOfEl.DestType:=TPasClassType(Data.Found);
|
|
ClassOfEl.DestType.AddRef;
|
|
UnresolvedEl.Release;
|
|
end
|
|
else
|
|
begin
|
|
// class-of has found a type
|
|
// another later in the same type section has priority -> check
|
|
OldClassType:=ClassOfEl.DestType as TPasClassType;
|
|
if ClassOfEl.DestType.Parent=ClassOfEl.Parent then
|
|
continue; // class in same type section -> ok
|
|
// class not in same type section -> check
|
|
ClassOfName:=OldClassType.Name;
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.FinishTypeSection resolving "',ClassOfEl.Name,'" = class of resolved "',ClassOfName,'"');
|
|
{$ENDIF}
|
|
Data.ErrorPosEl:=ClassOfEl;
|
|
Abort:=false;
|
|
(TopScope as TPasIdentifierScope).IterateElements(ClassOfName,
|
|
TopScope,@OnFindFirstElement,@Data,Abort);
|
|
if (Data.Found=nil) then
|
|
continue;
|
|
if Data.Found.ClassType<>TPasClassType then
|
|
RaiseXExpectedButYFound(20170221171040,'class',Data.Found.ElementTypeName,ClassOfEl);
|
|
ClassOfEl.DestType:=TPasClassType(Data.Found);
|
|
ClassOfEl.DestType.AddRef;
|
|
OldClassType.Release;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.FinishTypeDef(El: TPasType);
|
|
var
|
|
C: TClass;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.FinishTypeDef El=',GetObjName(El));
|
|
{$ENDIF}
|
|
C:=El.ClassType;
|
|
if C=TPasEnumType then
|
|
FinishEnumType(TPasEnumType(El))
|
|
else if C=TPasSetType then
|
|
FinishSetType(TPasSetType(El))
|
|
else if C=TPasRangeType then
|
|
FinishRangeType(TPasRangeType(El))
|
|
else if C=TPasRecordType then
|
|
FinishRecordType(TPasRecordType(El))
|
|
else if C=TPasClassType then
|
|
FinishClassType(TPasClassType(El))
|
|
else if C=TPasClassOfType then
|
|
FinishClassOfType(TPasClassOfType(El))
|
|
else if C=TPasArrayType then
|
|
FinishArrayType(TPasArrayType(El));
|
|
end;
|
|
|
|
procedure TPasResolver.FinishEnumType(El: TPasEnumType);
|
|
begin
|
|
if TopScope.Element=El then
|
|
PopScope;
|
|
end;
|
|
|
|
procedure TPasResolver.FinishSetType(El: TPasSetType);
|
|
var
|
|
BaseTypeData: TResElDataBaseType;
|
|
StartResolved, EndResolved: TPasResolverResult;
|
|
RangeExpr: TBinaryExpr;
|
|
C: TClass;
|
|
EnumType: TPasType;
|
|
begin
|
|
EnumType:=El.EnumType;
|
|
C:=EnumType.ClassType;
|
|
if C=TPasEnumType then
|
|
begin
|
|
FinishSubElementType(El,EnumType);
|
|
exit;
|
|
end
|
|
else if C=TPasRangeType then
|
|
begin
|
|
RangeExpr:=TPasRangeType(EnumType).RangeExpr;
|
|
if RangeExpr.Parent=El then
|
|
CheckRangeExpr(RangeExpr.left,RangeExpr.right,StartResolved,EndResolved);
|
|
FinishSubElementType(El,EnumType);
|
|
exit;
|
|
end
|
|
else if C=TPasUnresolvedSymbolRef then
|
|
begin
|
|
if EnumType.CustomData is TResElDataBaseType then
|
|
begin
|
|
BaseTypeData:=TResElDataBaseType(EnumType.CustomData);
|
|
if BaseTypeData.BaseType in [btChar,btBoolean] then
|
|
exit;
|
|
RaiseXExpectedButYFound(20170216151553,'char or boolean',EnumType.ElementTypeName,EnumType);
|
|
end;
|
|
end;
|
|
RaiseXExpectedButYFound(20170216151557,'enum type',EnumType.ElementTypeName,EnumType);
|
|
end;
|
|
|
|
procedure TPasResolver.FinishSubElementType(Parent, El: TPasElement);
|
|
var
|
|
Decl: TPasDeclarations;
|
|
EnumScope: TPasEnumTypeScope;
|
|
begin
|
|
if (El.Name<>'') or (AnonymousElTypePostfix='') then exit;
|
|
if Parent.Name='' then
|
|
RaiseMsg(20170415165455,nCannotNestAnonymousX,sCannotNestAnonymousX,[El.ElementTypeName],El);
|
|
if not (Parent.Parent is TPasDeclarations) then
|
|
RaiseMsg(20170416094735,nCannotNestAnonymousX,sCannotNestAnonymousX,[El.ElementTypeName],El);
|
|
// give anonymous sub type a name
|
|
El.Name:=Parent.Name+AnonymousElTypePostfix;
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.FinishSubElementType parent="',GetObjName(Parent),'" named anonymous type "',GetObjName(El),'"');
|
|
{$ENDIF}
|
|
Decl:=TPasDeclarations(Parent.Parent);
|
|
Decl.Declarations.Add(El);
|
|
El.AddRef;
|
|
El.Parent:=Decl;
|
|
Decl.Types.Add(El);
|
|
if (El.ClassType=TPasEnumType) and (Parent.ClassType=TPasSetType) then
|
|
begin
|
|
EnumScope:=TPasEnumTypeScope(El.CustomData);
|
|
if EnumScope.CanonicalSet<>Parent then
|
|
begin
|
|
ReleaseAndNil(TPasElement(EnumScope.CanonicalSet));
|
|
EnumScope.CanonicalSet:=TPasSetType(Parent);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.FinishRangeType(El: TPasRangeType);
|
|
var
|
|
StartResolved, EndResolved: TPasResolverResult;
|
|
begin
|
|
ResolveExpr(El.RangeExpr.left,rraRead);
|
|
ResolveExpr(El.RangeExpr.right,rraRead);
|
|
CheckRangeExpr(El.RangeExpr.left,El.RangeExpr.right,StartResolved,EndResolved);
|
|
end;
|
|
|
|
procedure TPasResolver.FinishRecordType(El: TPasRecordType);
|
|
begin
|
|
if TopScope.Element=El then
|
|
PopScope;
|
|
end;
|
|
|
|
procedure TPasResolver.FinishClassType(El: TPasClassType);
|
|
begin
|
|
if TopScope.Element=El then
|
|
PopScope;
|
|
end;
|
|
|
|
procedure TPasResolver.FinishClassOfType(El: TPasClassOfType);
|
|
begin
|
|
if El.DestType is TUnresolvedPendingRef then exit;
|
|
if El.DestType is TPasClassType then exit;
|
|
RaiseMsg(20170216151602,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected,
|
|
[El.DestType.Name,'class'],El);
|
|
end;
|
|
|
|
procedure TPasResolver.FinishArrayType(El: TPasArrayType);
|
|
var
|
|
i: Integer;
|
|
Expr: TPasExpr;
|
|
RangeResolved: TPasResolverResult;
|
|
begin
|
|
for i:=0 to length(El.Ranges)-1 do
|
|
begin
|
|
Expr:=El.Ranges[i];
|
|
ResolveExpr(Expr,rraRead);
|
|
ComputeElement(Expr,RangeResolved,[rcConstant]);
|
|
if (RangeResolved.IdentEl<>nil) and not (RangeResolved.IdentEl is TPasType) then
|
|
RaiseXExpectedButYFound(20170216151607,'range',RangeResolved.IdentEl.ElementTypeName,Expr);
|
|
if (RangeResolved.BaseType=btRange) and (RangeResolved.SubType in btArrayRangeTypes) then
|
|
// range, e.g. 1..2
|
|
else if RangeResolved.BaseType in btArrayRangeTypes then
|
|
// full range, e.g. array[char]
|
|
else if (RangeResolved.BaseType=btContext) and (RangeResolved.TypeEl is TPasEnumType) then
|
|
// e.g. array[enumtype]
|
|
else
|
|
RaiseXExpectedButYFound(20170216151609,'range',RangeResolved.IdentEl.ElementTypeName,Expr);
|
|
end;
|
|
FinishSubElementType(El,El.ElType);
|
|
end;
|
|
|
|
procedure TPasResolver.FinishConstDef(El: TPasConst);
|
|
begin
|
|
ResolveExpr(El.Expr,rraRead);
|
|
if El.VarType<>nil then
|
|
CheckAssignCompatibility(El,El.Expr,true);
|
|
end;
|
|
|
|
procedure TPasResolver.FinishProcedure(aProc: TPasProcedure);
|
|
var
|
|
i: Integer;
|
|
Body: TProcedureBody;
|
|
SubEl: TPasElement;
|
|
SubProcScope: TPasProcedureScope;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.FinishProcedure START');
|
|
{$ENDIF}
|
|
CheckTopScope(TPasProcedureScope);
|
|
if TPasProcedureScope(TopScope).Element<>aProc then
|
|
RaiseInternalError(20170220163043);
|
|
Body:=aProc.Body;
|
|
if Body<>nil then
|
|
begin
|
|
ResolveImplBlock(Body.Body);
|
|
|
|
// check if all forward procs are resolved
|
|
for i:=0 to Body.Declarations.Count-1 do
|
|
begin
|
|
SubEl:=TPasElement(Body.Declarations[i]);
|
|
if (SubEl is TPasProcedure) and TPasProcedure(SubEl).IsForward then
|
|
begin
|
|
SubProcScope:=TPasProcedure(SubEl).CustomData as TPasProcedureScope;
|
|
if SubProcScope.ImplProc=nil then
|
|
RaiseMsg(20170216151613,nForwardProcNotResolved,sForwardProcNotResolved,
|
|
[SubEl.ElementTypeName,SubEl.Name],SubEl);
|
|
end;
|
|
end;
|
|
end;
|
|
PopScope;
|
|
end;
|
|
|
|
procedure TPasResolver.FinishProcedureType(El: TPasProcedureType);
|
|
var
|
|
ProcName: String;
|
|
FindData: TFindOverloadProcData;
|
|
DeclProc, Proc, ParentProc: TPasProcedure;
|
|
Abort: boolean;
|
|
DeclProcScope, ProcScope: TPasProcedureScope;
|
|
ParentScope: TPasScope;
|
|
pm: TProcedureModifier;
|
|
ptm: TProcTypeModifier;
|
|
begin
|
|
if (El.Parent is TPasProcedure) and (TPasProcedure(El.Parent).ProcType=El) then
|
|
begin
|
|
// finished header of a procedure declaration
|
|
// -> search the best fitting proc
|
|
CheckTopScope(TPasProcedureScope);
|
|
Proc:=TPasProcedure(El.Parent);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.FinishProcedureHeader El=',GetTreeDesc(El),' ',GetElementSourcePosStr(El),' IsForward=',Proc.IsForward,' Parent=',GetObjName(El.Parent));
|
|
{$ENDIF}
|
|
ProcName:=Proc.Name;
|
|
|
|
if (proProcTypeWithoutIsNested in Options) and El.IsNested then
|
|
RaiseMsg(20170402120811,nIllegalQualifier,sIllegalQualifier,['is nested'],El);
|
|
|
|
if (Proc.Parent.ClassType=TProcedureBody) then
|
|
begin
|
|
// nested sub proc
|
|
if not (proProcTypeWithoutIsNested in Options) then
|
|
El.IsNested:=true;
|
|
// inherit 'of Object'
|
|
ParentProc:=Proc.Parent.Parent as TPasProcedure;
|
|
if ParentProc.ProcType.IsOfObject then
|
|
El.IsOfObject:=true;
|
|
end;
|
|
|
|
if Proc.IsExternal then
|
|
begin
|
|
for pm in TProcedureModifier do
|
|
if (pm in Proc.Modifiers)
|
|
and not (pm in [pmVirtual, pmDynamic, pmOverride,
|
|
pmOverload, pmMessage, pmReintroduce,
|
|
pmExternal, pmDispId,
|
|
pmfar]) then
|
|
RaiseMsg(20170216151616,nInvalidXModifierY,
|
|
sInvalidXModifierY,[Proc.ElementTypeName,'external, '+ModifierNames[pm]],Proc);
|
|
for ptm in TProcTypeModifier do
|
|
if (ptm in Proc.ProcType.Modifiers)
|
|
and not (ptm in [ptmOfObject,ptmIsNested,ptmStatic,ptmVarargs]) then
|
|
RaiseMsg(20170411171224,nInvalidXModifierY,
|
|
sInvalidXModifierY,[Proc.ElementTypeName,'external, '+ProcTypeModifiers[ptm]],Proc);
|
|
end;
|
|
|
|
if Proc.Parent is TPasClassType then
|
|
begin
|
|
// method declaration
|
|
if Proc.IsAbstract then
|
|
begin
|
|
if not Proc.IsVirtual then
|
|
RaiseMsg(20170216151623,nInvalidXModifierY,sInvalidXModifierY,[Proc.ElementTypeName,'abstract without virtual'],Proc);
|
|
if Proc.IsOverride then
|
|
RaiseMsg(20170216151625,nInvalidXModifierY,sInvalidXModifierY,[Proc.ElementTypeName,'abstract, override'],Proc);
|
|
end;
|
|
if Proc.IsVirtual and Proc.IsOverride then
|
|
RaiseMsg(20170216151627,nInvalidXModifierY,sInvalidXModifierY,[Proc.ElementTypeName,'virtual, override'],Proc);
|
|
if Proc.IsForward then
|
|
RaiseMsg(20170216151629,nInvalidXModifierY,sInvalidXModifierY,[Proc.ElementTypeName,'forward'],Proc);
|
|
if Proc.IsStatic then
|
|
if (Proc.ClassType<>TPasClassProcedure) and (Proc.ClassType<>TPasClassFunction) then
|
|
RaiseMsg(20170216151631,nInvalidXModifierY,sInvalidXModifierY,[Proc.ElementTypeName,'static'],Proc);
|
|
end
|
|
else
|
|
begin
|
|
// intf proc, forward proc, proc body, method body
|
|
if Proc.IsAbstract then
|
|
RaiseMsg(20170216151634,nInvalidXModifierY,sInvalidXModifierY,[Proc.ElementTypeName,'abstract'],Proc);
|
|
if Proc.IsVirtual then
|
|
RaiseMsg(20170216151635,nInvalidXModifierY,sInvalidXModifierY,[Proc.ElementTypeName,'virtual'],Proc);
|
|
if Proc.IsOverride then
|
|
RaiseMsg(20170216151637,nInvalidXModifierY,sInvalidXModifierY,[Proc.ElementTypeName,'override'],Proc);
|
|
if Proc.IsMessage then
|
|
RaiseMsg(20170216151638,nInvalidXModifierY,sInvalidXModifierY,[Proc.ElementTypeName,'message'],Proc);
|
|
if Proc.IsStatic then
|
|
RaiseMsg(20170216151640,nInvalidXModifierY,sInvalidXModifierY,[Proc.ElementTypeName,'static'],Proc);
|
|
end;
|
|
|
|
if Pos('.',ProcName)>1 then
|
|
begin
|
|
FinishMethodImplHeader(Proc);
|
|
exit;
|
|
end;
|
|
|
|
// finish interface/implementation/nested procedure/method declaration
|
|
|
|
if not IsValidIdent(ProcName) then
|
|
RaiseNotYetImplemented(20160922163407,El);
|
|
|
|
if Proc.LibraryExpr<>nil then
|
|
ResolveExpr(Proc.LibraryExpr,rraRead);
|
|
if Proc.LibrarySymbolName<>nil then
|
|
ResolveExpr(Proc.LibrarySymbolName,rraRead);
|
|
|
|
if Proc.Parent is TPasClassType then
|
|
begin
|
|
FinishMethodDeclHeader(Proc);
|
|
exit;
|
|
end;
|
|
|
|
// finish interface/implementation/nested procedure
|
|
FindData:=Default(TFindOverloadProcData);
|
|
FindData.Proc:=Proc;
|
|
FindData.Args:=Proc.ProcType.Args;
|
|
Abort:=false;
|
|
IterateElements(ProcName,@OnFindOverloadProc,@FindData,Abort);
|
|
if FindData.FoundNonProc<>nil then
|
|
begin
|
|
// proc hides a non proc -> forbidden within module
|
|
if (Proc.GetModule=FindData.FoundNonProc.GetModule) then
|
|
RaiseMsg(20170216151649,nDuplicateIdentifier,sDuplicateIdentifier,
|
|
[FindData.FoundNonProc.Name,GetElementSourcePosStr(FindData.FoundNonProc)],Proc.ProcType);
|
|
end;
|
|
if FindData.Found=nil then
|
|
exit; // no overload -> ok
|
|
|
|
// overload found with same signature
|
|
DeclProc:=FindData.Found;
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.FinishProcedureHeader overload found: Proc2=',GetTreeDesc(DeclProc),' ',GetElementSourcePosStr(DeclProc),' IsForward=',DeclProc.IsForward,' Parent=',GetObjName(DeclProc.Parent));
|
|
{$ENDIF}
|
|
if (Proc.Parent=DeclProc.Parent)
|
|
or ((Proc.Parent is TImplementationSection)
|
|
and (DeclProc.Parent is TInterfaceSection)
|
|
and (Proc.Parent.Parent=DeclProc.Parent.Parent))
|
|
then
|
|
begin
|
|
// both procs are defined in the same scope
|
|
if ProcNeedsImplProc(Proc) or (not ProcNeedsImplProc(DeclProc)) then
|
|
RaiseMsg(20170216151652,nDuplicateIdentifier,sDuplicateIdentifier,
|
|
[ProcName,GetElementSourcePosStr(DeclProc)],Proc.ProcType);
|
|
CheckProcSignatureMatch(DeclProc,Proc);
|
|
DeclProcScope:=DeclProc.CustomData as TPasProcedureScope;
|
|
DeclProcScope.ImplProc:=Proc;
|
|
ProcScope:=Proc.CustomData as TPasProcedureScope;
|
|
ProcScope.DeclarationProc:=DeclProc;
|
|
// remove ImplProc from scope
|
|
ParentScope:=Scopes[ScopeCount-2];
|
|
(ParentScope as TPasIdentifierScope).RemoveLocalIdentifier(Proc);
|
|
// replace arguments with declaration arguments
|
|
ReplaceProcScopeImplArgsWithDeclArgs(ProcScope);
|
|
end
|
|
else
|
|
begin
|
|
// give a hint, that proc is hiding DeclProc
|
|
LogMsg(20170216151656,mtHint,nFunctionHidesIdentifier,sFunctionHidesIdentifier,
|
|
[DeclProc.Name,GetElementSourcePosStr(DeclProc)],Proc.ProcType);
|
|
end;
|
|
end
|
|
else if El.Name<>'' then
|
|
begin
|
|
// finished proc type, e.g. type TProcedure = procedure;
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20160922163411,El.Parent);
|
|
end;
|
|
|
|
procedure TPasResolver.FinishMethodDeclHeader(Proc: TPasProcedure);
|
|
|
|
procedure VisibilityLowered(Proc, OverloadProc: TPasProcedure);
|
|
begin
|
|
LogMsg(20170325004215,mtNote,nVirtualMethodXHasLowerVisibility,
|
|
sVirtualMethodXHasLowerVisibility,[Proc.Name,
|
|
VisibilityNames[Proc.Visibility],OverloadProc.Parent.Name,
|
|
VisibilityNames[OverloadProc.Visibility]],Proc);
|
|
Proc.Visibility:=OverloadProc.Visibility;
|
|
end;
|
|
|
|
var
|
|
Abort: boolean;
|
|
ClassScope: TPasClassScope;
|
|
FindData: TFindOverloadProcData;
|
|
OverloadProc: TPasProcedure;
|
|
ProcScope: TPasProcedureScope;
|
|
begin
|
|
Proc.ProcType.IsOfObject:=true;
|
|
ProcScope:=TopScope as TPasProcedureScope;
|
|
ClassScope:=Scopes[ScopeCount-2] as TPasClassScope;
|
|
ProcScope.ClassScope:=ClassScope;
|
|
FindData:=Default(TFindOverloadProcData);
|
|
FindData.Proc:=Proc;
|
|
FindData.Args:=Proc.ProcType.Args;
|
|
Abort:=false;
|
|
ClassScope.IterateElements(Proc.Name,ClassScope,@OnFindOverloadProc,@FindData,Abort);
|
|
if FindData.FoundNonProc<>nil then
|
|
// proc hides a non proc -> duplicate
|
|
RaiseMsg(20170216151659,nDuplicateIdentifier,sDuplicateIdentifier,
|
|
[FindData.FoundNonProc.Name,GetElementSourcePosStr(FindData.FoundNonProc)],Proc.ProcType);
|
|
if FindData.Found=nil then
|
|
begin
|
|
// no overload
|
|
if Proc.IsOverride then
|
|
RaiseMsg(20170216151702,nNoMethodInAncestorToOverride,
|
|
sNoMethodInAncestorToOverride,[GetProcDesc(Proc.ProcType)],Proc.ProcType);
|
|
end
|
|
else
|
|
begin
|
|
// overload found
|
|
OverloadProc:=FindData.Found;
|
|
if Proc.Parent=OverloadProc.Parent then
|
|
// overload in same scope -> duplicate
|
|
RaiseMsg(20170216151705,nDuplicateIdentifier,sDuplicateIdentifier,
|
|
[OverloadProc.Name,GetElementSourcePosStr(OverloadProc)],Proc.ProcType);
|
|
ProcScope.OverriddenProc:=OverloadProc;
|
|
if Proc.IsOverride then
|
|
begin
|
|
if (not OverloadProc.IsVirtual) and (not OverloadProc.IsOverride) then
|
|
// the OverloadProc fits the signature, but is not virtual
|
|
RaiseMsg(20170216151708,nNoMethodInAncestorToOverride,
|
|
sNoMethodInAncestorToOverride,[GetProcDesc(Proc.ProcType)],Proc.ProcType);
|
|
// override a virtual method
|
|
CheckProcSignatureMatch(OverloadProc,Proc);
|
|
// check visibility
|
|
if Proc.Visibility<>OverloadProc.Visibility then
|
|
case Proc.Visibility of
|
|
visPrivate,visStrictPrivate:
|
|
if not (OverloadProc.Visibility in [visPrivate,visStrictPrivate]) then
|
|
VisibilityLowered(Proc,OverloadProc);
|
|
visProtected,visStrictProtected:
|
|
if not (OverloadProc.Visibility in [visPrivate,visProtected,visStrictPrivate,visStrictProtected]) then
|
|
VisibilityLowered(Proc,OverloadProc);
|
|
visPublic:
|
|
if not (OverloadProc.Visibility in [visPrivate..visPublic,visStrictPrivate,visStrictProtected]) then
|
|
VisibilityLowered(Proc,OverloadProc);
|
|
visPublished: ;
|
|
else
|
|
RaiseNotYetImplemented(20170325003315,Proc,'visibility');
|
|
end;
|
|
// check name case
|
|
if proFixCaseOfOverrides in Options then
|
|
Proc.Name:=OverloadProc.Name;
|
|
end
|
|
else if not Proc.IsReintroduced then
|
|
begin
|
|
// give a hint, that proc is hiding OverloadProc
|
|
LogMsg(20170216151712,mtHint,nFunctionHidesIdentifier,sFunctionHidesIdentifier,
|
|
[OverloadProc.Name,GetElementSourcePosStr(OverloadProc)],Proc.ProcType);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.FinishMethodImplHeader(ImplProc: TPasProcedure);
|
|
var
|
|
ProcName: String;
|
|
CurClassType: TPasClassType;
|
|
FindData: TFindOverloadProcData;
|
|
Abort: boolean;
|
|
ImplProcScope, DeclProcScope: TPasProcedureScope;
|
|
DeclProc: TPasProcedure;
|
|
CurClassScope: TPasClassScope;
|
|
SelfArg: TPasArgument;
|
|
p: Integer;
|
|
begin
|
|
if ImplProc.IsExternal then
|
|
RaiseMsg(20170216151715,nInvalidXModifierY,sInvalidXModifierY,[ImplProc.ElementTypeName,'external'],ImplProc);
|
|
if ImplProc.IsExported then
|
|
RaiseMsg(20170216151717,nInvalidXModifierY,sInvalidXModifierY,[ImplProc.ElementTypeName,'export'],ImplProc);
|
|
|
|
ProcName:=ImplProc.Name;
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.FinishMethodBodyHeader searching declaration "',ProcName,'" ...');
|
|
{$ENDIF}
|
|
ImplProc.ProcType.IsOfObject:=true;
|
|
|
|
repeat
|
|
p:=Pos('.',ProcName);
|
|
if p<1 then break;
|
|
Delete(ProcName,1,p);
|
|
until false;
|
|
|
|
// search ImplProc in class
|
|
if not IsValidIdent(ProcName) then
|
|
RaiseNotYetImplemented(20160922163421,ImplProc.ProcType);
|
|
|
|
// search proc in class
|
|
ImplProcScope:=ImplProc.CustomData as TPasProcedureScope;
|
|
CurClassScope:=ImplProcScope.ClassScope;
|
|
if CurClassScope=nil then
|
|
RaiseInternalError(20161013172346);
|
|
CurClassType:=CurClassScope.Element as TPasClassType;
|
|
FindData:=Default(TFindOverloadProcData);
|
|
FindData.Proc:=ImplProc;
|
|
FindData.Args:=ImplProc.ProcType.Args;
|
|
FindData.OnlyScope:=CurClassScope;
|
|
Abort:=false;
|
|
CurClassScope.IterateElements(ProcName,CurClassScope,@OnFindOverloadProc,@FindData,Abort);
|
|
if FindData.Found=nil then
|
|
RaiseIdentifierNotFound(20170216151720,ImplProc.Name,ImplProc.ProcType);
|
|
|
|
// connect method declaration and body
|
|
DeclProc:=FindData.Found;
|
|
if DeclProc.IsAbstract then
|
|
RaiseMsg(20170216151722,nAbstractMethodsMustNotHaveImplementation,sAbstractMethodsMustNotHaveImplementation,[],ImplProc);
|
|
if DeclProc.IsExternal then
|
|
RaiseXExpectedButYFound(20170216151725,'method','external method',ImplProc);
|
|
CheckProcSignatureMatch(DeclProc,ImplProc);
|
|
ImplProcScope.DeclarationProc:=DeclProc;
|
|
DeclProcScope:=DeclProc.CustomData as TPasProcedureScope;
|
|
DeclProcScope.ImplProc:=ImplProc;
|
|
|
|
// replace arguments in scope with declaration arguments
|
|
ReplaceProcScopeImplArgsWithDeclArgs(ImplProcScope);
|
|
|
|
if not DeclProc.IsStatic then
|
|
begin
|
|
// add 'Self'
|
|
if (DeclProc.ClassType=TPasClassConstructor)
|
|
or (DeclProc.ClassType=TPasClassDestructor)
|
|
or (DeclProc.ClassType=TPasClassProcedure)
|
|
or (DeclProc.ClassType=TPasClassFunction) then
|
|
begin
|
|
// 'Self' in a class proc is the class VMT
|
|
AddIdentifier(ImplProcScope,'Self',CurClassType,pikSimple);
|
|
end
|
|
else
|
|
begin
|
|
// 'Self' in a proc is the hidden instance argument
|
|
SelfArg:=TPasArgument.Create('Self',DeclProc);
|
|
ImplProcScope.SelfArg:=SelfArg;
|
|
SelfArg.Access:=argConst;
|
|
SelfArg.ArgType:=CurClassType;
|
|
CurClassType.AddRef;
|
|
AddIdentifier(ImplProcScope,'Self',SelfArg,pikSimple);
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.FinishMethodBodyHeader END of searching proc "',ImplProc.Name,'" ...');
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TPasResolver.FinishExceptOnExpr;
|
|
var
|
|
El: TPasImplExceptOn;
|
|
ResolvedType: TPasResolverResult;
|
|
begin
|
|
CheckTopScope(TPasExceptOnScope);
|
|
El:=TPasImplExceptOn(FTopScope.Element);
|
|
ComputeElement(El.TypeEl,ResolvedType,[rcSkipTypeAlias,rcType]);
|
|
CheckIsClass(El.TypeEl,ResolvedType);
|
|
end;
|
|
|
|
procedure TPasResolver.FinishExceptOnStatement;
|
|
begin
|
|
//writeln('TPasResolver.FinishExceptOnStatement START');
|
|
CheckTopScope(TPasExceptOnScope);
|
|
ResolveImplElement(TPasImplExceptOn(FTopScope.Element).Body);
|
|
PopScope;
|
|
end;
|
|
|
|
procedure TPasResolver.FinishDeclaration(El: TPasElement);
|
|
var
|
|
C: TClass;
|
|
begin
|
|
C:=El.ClassType;
|
|
if C=TPasVariable then
|
|
FinishVariable(TPasVariable(El))
|
|
else if C=TPasProperty then
|
|
FinishPropertyOfClass(TPasProperty(El))
|
|
else if C=TPasArgument then
|
|
FinishArgument(TPasArgument(El));
|
|
end;
|
|
|
|
procedure TPasResolver.FinishVariable(El: TPasVariable);
|
|
begin
|
|
if (El.Visibility=visPublished) then
|
|
begin
|
|
if [vmClass,vmStatic,vmCVar]*El.VarModifiers<>[] then
|
|
RaiseMsg(20170403223837,nSymbolCannotBePublished,sSymbolCannotBePublished,[],El);
|
|
end;
|
|
if El.Expr<>nil then
|
|
begin
|
|
ResolveExpr(El.Expr,rraRead);
|
|
CheckAssignCompatibility(El,El.Expr,true);
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.FinishPropertyOfClass(PropEl: TPasProperty);
|
|
var
|
|
PropType: TPasType;
|
|
ClassScope: TPasClassScope;
|
|
|
|
procedure GetPropType;
|
|
var
|
|
AncEl: TPasElement;
|
|
AncProp: TPasProperty;
|
|
begin
|
|
if PropType<>nil then exit;
|
|
if PropEl.VarType<>nil then
|
|
PropType:=PropEl.VarType
|
|
// Note: a property with a type has no ancestor property
|
|
else
|
|
begin
|
|
// search property in ancestor
|
|
AncEl:=nil;
|
|
if ClassScope.AncestorScope<>nil then
|
|
AncEl:=ClassScope.AncestorScope.FindElement(PropEl.Name);
|
|
if (not (AncEl is TPasProperty)) then
|
|
RaiseMsg(20170216151741,nNoPropertyFoundToOverride,sNoPropertyFoundToOverride,[],PropEl);
|
|
// found -> create reference
|
|
AncProp:=TPasProperty(AncEl);
|
|
(PropEl.CustomData as TPasPropertyScope).AncestorProp:=AncProp;
|
|
AncProp.AddRef;
|
|
// check property versus class property
|
|
if PropEl.ClassType<>AncProp.ClassType then
|
|
RaiseXExpectedButYFound(20170216151744,AncProp.ElementTypeName,PropEl.ElementTypeName,PropEl);
|
|
// get inherited type
|
|
PropType:=GetPasPropertyType(AncProp);
|
|
// update DefaultProperty
|
|
if (ClassScope.DefaultProperty=AncProp) then
|
|
ClassScope.DefaultProperty:=PropEl;
|
|
end;
|
|
end;
|
|
|
|
function GetAccessor(Expr: TPasExpr): TPasElement;
|
|
var
|
|
Prim: TPrimitiveExpr;
|
|
DeclEl: TPasElement;
|
|
Identifier: TPasIdentifier;
|
|
Scope: TPasIdentifierScope;
|
|
begin
|
|
if Expr.ClassType=TBinaryExpr then
|
|
begin
|
|
if (TBinaryExpr(Expr).left is TPrimitiveExpr) then
|
|
begin
|
|
Prim:=TPrimitiveExpr(TBinaryExpr(Expr).left);
|
|
if Prim.Kind<>pekIdent then
|
|
RaiseXExpectedButYFound(20170216151746,'class',Prim.Value,Prim);
|
|
Scope:=TopScope as TPasIdentifierScope;
|
|
// search in class and ancestors, not in unit interface
|
|
Identifier:=Scope.FindIdentifier(Prim.Value);
|
|
if Identifier=nil then
|
|
RaiseIdentifierNotFound(20170216151749,Prim.Value,Prim);
|
|
DeclEl:=Identifier.Element;
|
|
if DeclEl.ClassType<>TPasClassType then
|
|
RaiseXExpectedButYFound(20170216151752,'class',DeclEl.ElementTypeName,Prim);
|
|
CreateReference(DeclEl,Prim,rraRead);
|
|
end
|
|
else
|
|
RaiseMsg(20170216151754,nIllegalQualifier,sIllegalQualifier,[OpcodeStrings[TBinaryExpr(Expr).OpCode]],Expr);
|
|
if TBinaryExpr(Expr).OpCode<>eopSubIdent then
|
|
RaiseMsg(20170216151757,nIllegalQualifier,sIllegalQualifier,[OpcodeStrings[TBinaryExpr(Expr).OpCode]],Expr);
|
|
PushClassDotScope(TPasClassType(DeclEl));
|
|
Expr:=TBinaryExpr(Expr).right;
|
|
Result:=GetAccessor(Expr);
|
|
PopScope;
|
|
end
|
|
else if Expr.ClassType=TPrimitiveExpr then
|
|
begin
|
|
Prim:=TPrimitiveExpr(Expr);
|
|
if Prim.Kind<>pekIdent then
|
|
RaiseXExpectedButYFound(20170216151800,'identifier',Prim.Value,Prim);
|
|
Scope:=TopScope as TPasIdentifierScope;
|
|
// search in class and ancestors, not in unit interface
|
|
Identifier:=Scope.FindIdentifier(Prim.Value);
|
|
if Identifier=nil then
|
|
RaiseIdentifierNotFound(20170216151803,Prim.Value,Prim);
|
|
DeclEl:=Identifier.Element;
|
|
CreateReference(DeclEl,Prim,rraRead);
|
|
Result:=DeclEl;
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20160922163436,Expr);
|
|
end;
|
|
|
|
procedure CheckArgs(Proc: TPasProcedure; ErrorEl: TPasElement);
|
|
var
|
|
ArgNo: Integer;
|
|
PropArg, ProcArg: TPasArgument;
|
|
PropArgResolved, ProcArgResolved: TPasResolverResult;
|
|
begin
|
|
ArgNo:=0;
|
|
while ArgNo<PropEl.Args.Count do
|
|
begin
|
|
if ArgNo>=Proc.ProcType.Args.Count then
|
|
RaiseMsg(20170216151805,nWrongNumberOfParametersForCallTo,
|
|
sWrongNumberOfParametersForCallTo,[Proc.Name],ErrorEl);
|
|
PropArg:=TPasArgument(PropEl.Args[ArgNo]);
|
|
ProcArg:=TPasArgument(Proc.ProcType.Args[ArgNo]);
|
|
inc(ArgNo);
|
|
|
|
// check access: var, const, ...
|
|
if PropArg.Access<>ProcArg.Access then
|
|
RaiseMsg(20170216151808,nIncompatibleTypeArgNo,sIncompatibleTypeArgNo,
|
|
[IntToStr(ArgNo),AccessDescriptions[ProcArg.Access],
|
|
AccessDescriptions[PropArg.Access]],ErrorEl);
|
|
|
|
// check typed
|
|
if PropArg.ArgType=nil then
|
|
begin
|
|
if ProcArg.ArgType<>nil then
|
|
RaiseMsg(20170216151811,nIncompatibleTypeArgNo,sIncompatibleTypeArgNo,
|
|
[IntToStr(ArgNo),ProcArg.ArgType.ElementTypeName,'untyped'],ErrorEl);
|
|
end
|
|
else if ProcArg.ArgType=nil then
|
|
RaiseMsg(20170216151813,nIncompatibleTypeArgNo,sIncompatibleTypeArgNo,
|
|
[IntToStr(ArgNo),'untyped',PropArg.ArgType.ElementTypeName],ErrorEl)
|
|
else
|
|
begin
|
|
ComputeElement(PropArg,PropArgResolved,[rcNoImplicitProc]);
|
|
ComputeElement(ProcArg,ProcArgResolved,[rcNoImplicitProc]);
|
|
|
|
if (PropArgResolved.BaseType<>ProcArgResolved.BaseType) then
|
|
RaiseMsg(20170216151816,nIncompatibleTypeArgNo,sIncompatibleTypeArgNo,
|
|
[IntToStr(ArgNo),BaseTypeNames[ProcArgResolved.BaseType],BaseTypeNames[PropArgResolved.BaseType]],ErrorEl);
|
|
if PropArgResolved.TypeEl=nil then
|
|
RaiseInternalError(20161010125255);
|
|
if ProcArgResolved.TypeEl=nil then
|
|
RaiseInternalError(20161010125304);
|
|
if (PropArgResolved.TypeEl<>ProcArgResolved.TypeEl) then
|
|
RaiseIncompatibleType(20170216151819,nIncompatibleTypeArgNo,
|
|
[IntToStr(ArgNo)],ProcArgResolved.TypeEl,PropArgResolved.TypeEl,ErrorEl);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
ResultType, TypeEl: TPasType;
|
|
CurClassType: TPasClassType;
|
|
AccEl: TPasElement;
|
|
Proc: TPasProcedure;
|
|
Arg: TPasArgument;
|
|
PropArgCount: Integer;
|
|
PropTypeResolved, DefaultResolved: TPasResolverResult;
|
|
m: TVariableModifier;
|
|
begin
|
|
CheckTopScope(TPasPropertyScope);
|
|
PopScope;
|
|
|
|
if PropEl.Visibility=visPublished then
|
|
for m in PropEl.VarModifiers do
|
|
if not (m in [vmExternal]) then
|
|
RaiseMsg(20170403224112,nInvalidXModifierY,sInvalidXModifierY,
|
|
['published property','"'+VariableModifierNames[m]+'"'],PropEl);
|
|
|
|
PropType:=nil;
|
|
CurClassType:=PropEl.Parent as TPasClassType;
|
|
ClassScope:=CurClassType.CustomData as TPasClassScope;
|
|
GetPropType;
|
|
if PropEl.IndexExpr<>nil then
|
|
begin
|
|
ResolveExpr(PropEl.IndexExpr,rraRead);
|
|
RaiseNotYetImplemented(20160922163439,PropEl.IndexExpr);
|
|
end;
|
|
if PropEl.ReadAccessor<>nil then
|
|
begin
|
|
// check compatibility
|
|
AccEl:=GetAccessor(PropEl.ReadAccessor);
|
|
if (AccEl.ClassType=TPasVariable) or (AccEl.ClassType=TPasConst) then
|
|
begin
|
|
if PropEl.Args.Count>0 then
|
|
RaiseXExpectedButYFound(20170216151823,'function',AccEl.ElementTypeName,PropEl.ReadAccessor);
|
|
if TPasVariable(AccEl).VarType<>PropType then
|
|
RaiseIncompatibleType(20170216151826,nIncompatibleTypesGotExpected,
|
|
[],PropType,TPasVariable(AccEl).VarType,PropEl.ReadAccessor);
|
|
if (vmClass in PropEl.VarModifiers)<>(vmClass in TPasVariable(AccEl).VarModifiers) then
|
|
if vmClass in PropEl.VarModifiers then
|
|
RaiseXExpectedButYFound(20170216151828,'class var','var',PropEl.ReadAccessor)
|
|
else
|
|
RaiseXExpectedButYFound(20170216151831,'var','class var',PropEl.ReadAccessor);
|
|
end
|
|
else if AccEl is TPasProcedure then
|
|
begin
|
|
// check function
|
|
Proc:=TPasProcedure(AccEl);
|
|
if (vmClass in PropEl.VarModifiers) then
|
|
begin
|
|
if Proc.ClassType<>TPasClassFunction then
|
|
RaiseXExpectedButYFound(20170216151834,'class function',Proc.ElementTypeName,PropEl.ReadAccessor);
|
|
if Proc.IsStatic=(proClassPropertyNonStatic in Options) then
|
|
if Proc.IsStatic then
|
|
RaiseMsg(20170216151837,nClassPropertyAccessorMustNotBeStatic,sClassPropertyAccessorMustNotBeStatic,[],PropEl.ReadAccessor)
|
|
else
|
|
RaiseMsg(20170216151839,nClassPropertyAccessorMustBeStatic,sClassPropertyAccessorMustBeStatic,[],PropEl.ReadAccessor);
|
|
end
|
|
else
|
|
begin
|
|
if Proc.ClassType<>TPasFunction then
|
|
RaiseXExpectedButYFound(20170216151842,'function',Proc.ElementTypeName,PropEl.ReadAccessor);
|
|
end;
|
|
// check function result type
|
|
ResultType:=TPasFunction(Proc).FuncType.ResultEl.ResultType;
|
|
if not IsSameType(ResultType,PropType) then
|
|
RaiseXExpectedButYFound(20170216151844,'function result '+GetTypeDesc(PropType,true),
|
|
GetTypeDesc(ResultType,true),PropEl.ReadAccessor);
|
|
// check args
|
|
CheckArgs(Proc,PropEl.ReadAccessor);
|
|
if Proc.ProcType.Args.Count<>PropEl.Args.Count then
|
|
RaiseMsg(20170216151847,nWrongNumberOfParametersForCallTo,sWrongNumberOfParametersForCallTo,
|
|
[Proc.Name],PropEl.ReadAccessor);
|
|
end
|
|
else
|
|
RaiseXExpectedButYFound(20170216151850,'variable',AccEl.ElementTypeName,PropEl.ReadAccessor);
|
|
end;
|
|
if PropEl.WriteAccessor<>nil then
|
|
begin
|
|
// check compatibility
|
|
AccEl:=GetAccessor(PropEl.WriteAccessor);
|
|
if AccEl.ClassType=TPasVariable then
|
|
begin
|
|
if PropEl.Args.Count>0 then
|
|
RaiseXExpectedButYFound(20170216151852,'procedure',AccEl.ElementTypeName,PropEl.WriteAccessor);
|
|
if TPasVariable(AccEl).VarType<>PropType then
|
|
RaiseIncompatibleType(20170216151855,nIncompatibleTypesGotExpected,
|
|
[],PropType,TPasVariable(AccEl).VarType,PropEl.WriteAccessor);
|
|
if (vmClass in PropEl.VarModifiers)<>(vmClass in TPasVariable(AccEl).VarModifiers) then
|
|
if vmClass in PropEl.VarModifiers then
|
|
RaiseXExpectedButYFound(20170216151858,'class var','var',PropEl.WriteAccessor)
|
|
else
|
|
RaiseXExpectedButYFound(20170216151900,'var','class var',PropEl.WriteAccessor);
|
|
end
|
|
else if AccEl is TPasProcedure then
|
|
begin
|
|
// check procedure
|
|
Proc:=TPasProcedure(AccEl);
|
|
if (vmClass in PropEl.VarModifiers) then
|
|
begin
|
|
if Proc.ClassType<>TPasClassProcedure then
|
|
RaiseXExpectedButYFound(20170216151903,'class procedure',Proc.ElementTypeName,PropEl.WriteAccessor);
|
|
if Proc.IsStatic=(proClassPropertyNonStatic in Options) then
|
|
if Proc.IsStatic then
|
|
RaiseMsg(20170216151905,nClassPropertyAccessorMustNotBeStatic,sClassPropertyAccessorMustNotBeStatic,[],PropEl.WriteAccessor)
|
|
else
|
|
RaiseMsg(20170216151906,nClassPropertyAccessorMustBeStatic,sClassPropertyAccessorMustBeStatic,[],PropEl.WriteAccessor);
|
|
end
|
|
else
|
|
begin
|
|
if Proc.ClassType<>TPasProcedure then
|
|
RaiseXExpectedButYFound(20170216151910,'procedure',Proc.ElementTypeName,PropEl.WriteAccessor);
|
|
end;
|
|
// check args
|
|
CheckArgs(Proc,PropEl.ReadAccessor);
|
|
PropArgCount:=PropEl.Args.Count;
|
|
if Proc.ProcType.Args.Count<>PropArgCount+1 then
|
|
RaiseMsg(20170216151913,nWrongNumberOfParametersForCallTo,sWrongNumberOfParametersForCallTo,
|
|
[Proc.Name],PropEl.WriteAccessor);
|
|
Arg:=TPasArgument(Proc.ProcType.Args[PropArgCount]);
|
|
if not (Arg.Access in [argDefault,argConst]) then
|
|
RaiseMsg(20170216151917,nIncompatibleTypeArgNo,sIncompatibleTypeArgNo,
|
|
[IntToStr(PropArgCount+1),AccessDescriptions[Arg.Access],
|
|
AccessDescriptions[argConst]],PropEl.WriteAccessor);
|
|
if Arg.ArgType<>PropType then
|
|
RaiseIncompatibleType(20170216151919,nIncompatibleTypeArgNo,
|
|
[IntToStr(PropArgCount+1)],Arg.ArgType,PropType,PropEl.WriteAccessor);
|
|
end
|
|
else
|
|
RaiseXExpectedButYFound(20170216151921,'variable',AccEl.ElementTypeName,PropEl.WriteAccessor);
|
|
end;
|
|
if PropEl.ImplementsFunc<>nil then
|
|
begin
|
|
ResolveExpr(PropEl.ImplementsFunc,rraRead);
|
|
// ToDo: check compatibility
|
|
RaiseNotYetImplemented(20170409213850,PropEl.ImplementsFunc);
|
|
end;
|
|
if PropEl.StoredAccessor<>nil then
|
|
begin
|
|
// check compatibility
|
|
AccEl:=GetAccessor(PropEl.StoredAccessor);
|
|
if (AccEl.ClassType=TPasVariable) or (AccEl.ClassType=TPasConst) then
|
|
begin
|
|
if PropEl.IndexExpr<>nil then
|
|
RaiseNotYetImplemented(20170409214006,PropEl.StoredAccessor,'stored with index');
|
|
TypeEl:=ResolveAliasType(TPasVariable(AccEl).VarType);
|
|
if not IsBaseType(TypeEl,btBoolean) then
|
|
RaiseIncompatibleType(20170409214300,nIncompatibleTypesGotExpected,
|
|
[],TypeEl,BaseTypes[btBoolean],PropEl.StoredAccessor);
|
|
if (vmClass in PropEl.VarModifiers)<>(vmClass in TPasVariable(AccEl).VarModifiers) then
|
|
if vmClass in PropEl.VarModifiers then
|
|
RaiseXExpectedButYFound(20170409214351,'class var','var',PropEl.StoredAccessor)
|
|
else
|
|
RaiseXExpectedButYFound(20170409214359,'var','class var',PropEl.StoredAccessor);
|
|
end
|
|
else if AccEl is TPasProcedure then
|
|
begin
|
|
// check function
|
|
Proc:=TPasProcedure(AccEl);
|
|
if Proc.ClassType<>TPasFunction then
|
|
RaiseXExpectedButYFound(20170216151925,'function',Proc.ElementTypeName,PropEl.StoredAccessor);
|
|
// check function result type
|
|
ResultType:=TPasFunction(Proc).FuncType.ResultEl.ResultType;
|
|
if not IsBaseType(ResultType,btBoolean) then
|
|
RaiseXExpectedButYFound(20170216151929,'function: boolean',
|
|
'function:'+GetTypeDesc(ResultType),PropEl.StoredAccessor);
|
|
// check arg count
|
|
if Proc.ProcType.Args.Count<>0 then
|
|
RaiseMsg(20170216151932,nWrongNumberOfParametersForCallTo,sWrongNumberOfParametersForCallTo,
|
|
[Proc.Name],PropEl.StoredAccessor);
|
|
end
|
|
else
|
|
RaiseXExpectedButYFound(20170216151935,'function: boolean',AccEl.ElementTypeName,PropEl.StoredAccessor);
|
|
end;
|
|
if PropEl.DefaultExpr<>nil then
|
|
begin
|
|
// check compatibility with type
|
|
ResolveExpr(PropEl.DefaultExpr,rraRead);
|
|
ComputeElement(PropEl.DefaultExpr,DefaultResolved,[rcConstant]);
|
|
ComputeElement(PropType,PropTypeResolved,[rcType]);
|
|
PropTypeResolved.IdentEl:=PropEl;
|
|
PropTypeResolved.Flags:=[rrfReadable];
|
|
CheckEqualResCompatibility(PropTypeResolved,DefaultResolved,PropEl.DefaultExpr,true);
|
|
end;
|
|
if PropEl.IsDefault then
|
|
begin
|
|
// set default array property
|
|
if (ClassScope.DefaultProperty<>nil)
|
|
and (ClassScope.DefaultProperty.Parent=PropEl.Parent) then
|
|
RaiseMsg(20170216151938,nOnlyOneDefaultPropertyIsAllowed,sOnlyOneDefaultPropertyIsAllowed,[],PropEl);
|
|
ClassScope.DefaultProperty:=PropEl;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.FinishArgument(El: TPasArgument);
|
|
begin
|
|
if El.ValueExpr<>nil then
|
|
begin
|
|
ResolveExpr(El.ValueExpr,rraRead);
|
|
if El.ArgType<>nil then
|
|
CheckAssignCompatibility(El,El.ValueExpr,true);
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.FinishAncestors(aClass: TPasClassType);
|
|
// called when the ancestor and interface list of a class has been parsed,
|
|
// before parsing the class elements
|
|
var
|
|
AncestorEl: TPasClassType;
|
|
ClassScope, AncestorClassScope: TPasClassScope;
|
|
DirectAncestor, AncestorType, El: TPasType;
|
|
i: Integer;
|
|
aModifier: String;
|
|
IsSealed: Boolean;
|
|
begin
|
|
if aClass.IsForward then
|
|
exit;
|
|
if aClass.ObjKind<>okClass then
|
|
RaiseNotYetImplemented(20161010174638,aClass,'Kind='+ObjKindNames[aClass.ObjKind]);
|
|
|
|
IsSealed:=false;
|
|
for i:=0 to aClass.Modifiers.Count-1 do
|
|
begin
|
|
aModifier:=lowercase(aClass.Modifiers[i]);
|
|
case aModifier of
|
|
'sealed': IsSealed:=true;
|
|
else
|
|
RaiseMsg(20170320190619,nIllegalQualifier,sIllegalQualifier,[aClass.Modifiers[i]],aClass);
|
|
end;
|
|
end;
|
|
|
|
DirectAncestor:=aClass.AncestorType;
|
|
AncestorType:=ResolveAliasType(DirectAncestor);
|
|
|
|
if AncestorType=nil then
|
|
begin
|
|
if (CompareText(aClass.Name,'TObject')=0) or aClass.IsExternal then
|
|
begin
|
|
// ok, no ancestors
|
|
AncestorEl:=nil;
|
|
end else begin
|
|
// search default ancestor TObject
|
|
AncestorEl:=TPasClassType(FindElementWithoutParams('TObject',aClass,false));
|
|
if not (AncestorEl is TPasClassType) then
|
|
RaiseXExpectedButYFound(20170216151941,'class type',GetObjName(AncestorEl),aClass);
|
|
if DirectAncestor=nil then
|
|
DirectAncestor:=AncestorEl;
|
|
end;
|
|
end
|
|
else if AncestorType.ClassType<>TPasClassType then
|
|
RaiseXExpectedButYFound(20170216151944,'class type',GetTypeDesc(AncestorType),aClass)
|
|
else
|
|
AncestorEl:=TPasClassType(AncestorType);
|
|
|
|
AncestorClassScope:=nil;
|
|
if AncestorEl=nil then
|
|
begin
|
|
// root class e.g. TObject
|
|
end
|
|
else
|
|
begin
|
|
// inherited class
|
|
if AncestorEl.IsForward then
|
|
RaiseMsg(20170216151947,nCantUseForwardDeclarationAsAncestor,
|
|
sCantUseForwardDeclarationAsAncestor,[AncestorEl.Name],aClass);
|
|
if aClass.IsExternal and not AncestorEl.IsExternal then
|
|
RaiseMsg(20170321144035,nAncestorIsNotExternal,sAncestorIsNotExternal,
|
|
[AncestorEl.Name],aClass);
|
|
AncestorClassScope:=AncestorEl.CustomData as TPasClassScope;
|
|
if pcsfSealed in AncestorClassScope.Flags then
|
|
RaiseMsg(20170320191735,nCannotCreateADescendantOfTheSealedClass,
|
|
sCannotCreateADescendantOfTheSealedClass,[AncestorEl.Name],aClass);
|
|
// check for cycle
|
|
El:=AncestorEl;
|
|
repeat
|
|
if El=aClass then
|
|
RaiseMsg(20170216151949,nAncestorCycleDetected,sAncestorCycleDetected,[],aClass);
|
|
if (El.ClassType=TPasAliasType)
|
|
or (El.ClassType=TPasTypeAliasType)
|
|
then
|
|
El:=TPasAliasType(El).DestType
|
|
else if El.ClassType=TPasClassType then
|
|
El:=TPasClassType(El).AncestorType;
|
|
until El=nil;
|
|
end;
|
|
|
|
// start scope for elements
|
|
{$IFDEF VerbosePasResolver}
|
|
//writeln('TPasResolver.FinishAncestors ',GetObjName(aClass.CustomData));
|
|
{$ENDIF}
|
|
PushScope(aClass,ScopeClass_Class);
|
|
ClassScope:=TPasClassScope(TopScope);
|
|
ClassScope.VisibilityContext:=aClass;
|
|
Include(ClassScope.Flags,pcsfAncestorResolved);
|
|
if IsSealed then
|
|
Include(ClassScope.Flags,pcsfSealed);
|
|
ClassScope.DirectAncestor:=DirectAncestor;
|
|
if AncestorEl<>nil then
|
|
begin
|
|
ClassScope.AncestorScope:=AncestorEl.CustomData as TPasClassScope;
|
|
ClassScope.DefaultProperty:=ClassScope.AncestorScope.DefaultProperty;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.FinishPropertyParamAccess(Params: TParamsExpr;
|
|
Prop: TPasProperty);
|
|
var
|
|
i: Integer;
|
|
ParamAccess: TResolvedRefAccess;
|
|
begin
|
|
for i:=0 to length(Params.Params)-1 do
|
|
begin
|
|
ParamAccess:=rraRead;
|
|
if i<Prop.Args.Count then
|
|
case TPasArgument(Prop.Args[i]).Access of
|
|
argVar: ParamAccess:=rraVarParam;
|
|
argOut: ParamAccess:=rraOutParam;
|
|
end;
|
|
AccessExpr(Params.Params[i],ParamAccess);
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.ReplaceProcScopeImplArgsWithDeclArgs(
|
|
ImplProcScope: TPasProcedureScope);
|
|
var
|
|
DeclProc, ImplProc: TPasProcedure;
|
|
DeclArgs, ImplArgs: TFPList;
|
|
i: Integer;
|
|
DeclArg, ImplArg: TPasArgument;
|
|
Identifier: TPasIdentifier;
|
|
begin
|
|
ImplProc:=ImplProcScope.Element as TPasProcedure;
|
|
ImplArgs:=ImplProc.ProcType.Args;
|
|
DeclProc:=ImplProcScope.DeclarationProc;
|
|
DeclArgs:=DeclProc.ProcType.Args;
|
|
for i:=0 to DeclArgs.Count-1 do
|
|
begin
|
|
DeclArg:=TPasArgument(DeclArgs[i]);
|
|
if i<ImplArgs.Count then
|
|
begin
|
|
ImplArg:=TPasArgument(ImplArgs[i]);
|
|
Identifier:=ImplProcScope.FindLocalIdentifier(DeclArg.Name);
|
|
//writeln('TPasResolver.ReplaceProcScopeImplArgsWithDeclArgs i=',i,' replacing ',GetObjName(ImplArg),' with ',GetObjName(DeclArg));
|
|
if Identifier.Element<>ImplArg then
|
|
RaiseInternalError(20170203161659,GetObjName(DeclArg)+' '+GetObjName(ImplArg));
|
|
Identifier.Element:=DeclArg;
|
|
Identifier.Identifier:=DeclArg.Name;
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20170203161826,ImplProc);
|
|
end;
|
|
if DeclProc is TPasFunction then
|
|
begin
|
|
// replace 'Result'
|
|
Identifier:=ImplProcScope.FindLocalIdentifier(ResolverResultVar);
|
|
if Identifier.Element is TPasResultElement then
|
|
Identifier.Element:=TPasFunction(DeclProc).FuncType.ResultEl;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.CheckProcSignatureMatch(DeclProc, ImplProc: TPasProcedure
|
|
);
|
|
var
|
|
i: Integer;
|
|
DeclArgs, ImplArgs: TFPList;
|
|
DeclName, ImplName: String;
|
|
ImplResult, DeclResult: TPasType;
|
|
begin
|
|
if ImplProc.ClassType<>DeclProc.ClassType then
|
|
RaiseXExpectedButYFound(20170216151729,DeclProc.TypeName,ImplProc.TypeName,ImplProc);
|
|
if ImplProc.CallingConvention<>DeclProc.CallingConvention then
|
|
RaiseMsg(20170216151731,nCallingConventionMismatch,sCallingConventionMismatch,[],ImplProc);
|
|
if ImplProc is TPasFunction then
|
|
begin
|
|
// check result type
|
|
ImplResult:=TPasFunction(ImplProc).FuncType.ResultEl.ResultType;
|
|
DeclResult:=TPasFunction(DeclProc).FuncType.ResultEl.ResultType;
|
|
|
|
if not CheckProcArgTypeCompatibility(ImplResult,DeclResult) then
|
|
RaiseIncompatibleType(20170216151734,nResultTypeMismatchExpectedButFound,
|
|
[],DeclResult,ImplResult,ImplProc);
|
|
end;
|
|
|
|
// check argument names
|
|
DeclArgs:=DeclProc.ProcType.Args;
|
|
ImplArgs:=ImplProc.ProcType.Args;
|
|
for i:=0 to DeclArgs.Count-1 do
|
|
begin
|
|
DeclName:=TPasArgument(DeclArgs[i]).Name;
|
|
ImplName:=TPasArgument(ImplArgs[i]).Name;
|
|
if CompareText(DeclName,ImplName)<>0 then
|
|
RaiseMsg(20170216151738,nFunctionHeaderMismatchForwardVarName,
|
|
sFunctionHeaderMismatchForwardVarName,[DeclProc.Name,DeclName,ImplName],ImplProc);
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveImplBlock(Block: TPasImplBlock);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if Block=nil then exit;
|
|
for i:=0 to Block.Elements.Count-1 do
|
|
ResolveImplElement(TPasImplElement(Block.Elements[i]));
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveImplElement(El: TPasImplElement);
|
|
var
|
|
C: TClass;
|
|
begin
|
|
//writeln('TPasResolver.ResolveImplElement ',GetObjName(El));
|
|
if El=nil then exit;
|
|
C:=El.ClassType;
|
|
if C=TPasImplBeginBlock then
|
|
ResolveImplBlock(TPasImplBeginBlock(El))
|
|
else if C=TPasImplAssign then
|
|
ResolveImplAssign(TPasImplAssign(El))
|
|
else if C=TPasImplSimple then
|
|
ResolveImplSimple(TPasImplSimple(El))
|
|
else if C=TPasImplBlock then
|
|
ResolveImplBlock(TPasImplBlock(El))
|
|
else if C=TPasImplRepeatUntil then
|
|
begin
|
|
ResolveImplBlock(TPasImplBlock(El));
|
|
ResolveStatementConditionExpr(TPasImplRepeatUntil(El).ConditionExpr);
|
|
end
|
|
else if C=TPasImplIfElse then
|
|
begin
|
|
ResolveStatementConditionExpr(TPasImplIfElse(El).ConditionExpr);
|
|
ResolveImplElement(TPasImplIfElse(El).IfBranch);
|
|
ResolveImplElement(TPasImplIfElse(El).ElseBranch);
|
|
end
|
|
else if C=TPasImplWhileDo then
|
|
begin
|
|
ResolveStatementConditionExpr(TPasImplWhileDo(El).ConditionExpr);
|
|
ResolveImplElement(TPasImplWhileDo(El).Body);
|
|
end
|
|
else if C=TPasImplCaseOf then
|
|
ResolveImplCaseOf(TPasImplCaseOf(El))
|
|
else if C=TPasImplLabelMark then
|
|
ResolveImplLabelMark(TPasImplLabelMark(El))
|
|
else if C=TPasImplForLoop then
|
|
ResolveImplForLoop(TPasImplForLoop(El))
|
|
else if C=TPasImplTry then
|
|
begin
|
|
ResolveImplBlock(TPasImplTry(El));
|
|
ResolveImplBlock(TPasImplTry(El).FinallyExcept);
|
|
ResolveImplBlock(TPasImplTry(El).ElseBranch);
|
|
end
|
|
else if C=TPasImplExceptOn then
|
|
// handled in FinishExceptOnStatement
|
|
else if C=TPasImplRaise then
|
|
ResolveImplRaise(TPasImplRaise(El))
|
|
else if C=TPasImplCommand then
|
|
begin
|
|
if TPasImplCommand(El).Command<>'' then
|
|
RaiseNotYetImplemented(20160922163442,El,'TPasResolver.ResolveImplElement');
|
|
end
|
|
else if C=TPasImplAsmStatement then
|
|
ResolveImplAsm(TPasImplAsmStatement(El))
|
|
else if C=TPasImplWithDo then
|
|
ResolveImplWithDo(TPasImplWithDo(El))
|
|
else
|
|
RaiseNotYetImplemented(20160922163445,El,'TPasResolver.ResolveImplElement');
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveImplCaseOf(CaseOf: TPasImplCaseOf);
|
|
var
|
|
i, j: Integer;
|
|
El: TPasElement;
|
|
Stat: TPasImplCaseStatement;
|
|
CaseExprResolved, OfExprResolved: TPasResolverResult;
|
|
OfExpr: TPasExpr;
|
|
ok: Boolean;
|
|
begin
|
|
ResolveExpr(CaseOf.CaseExpr,rraRead);
|
|
ComputeElement(CaseOf.CaseExpr,CaseExprResolved,[]);
|
|
ok:=false;
|
|
if (rrfReadable in CaseExprResolved.Flags) then
|
|
begin
|
|
if (CaseExprResolved.BaseType in (btAllInteger+btAllBooleans+btAllStringAndChars)) then
|
|
ok:=true
|
|
else if CaseExprResolved.BaseType=btContext then
|
|
begin
|
|
if CaseExprResolved.TypeEl.ClassType=TPasEnumType then
|
|
ok:=true;
|
|
end;
|
|
end;
|
|
if not ok then
|
|
RaiseXExpectedButYFound(20170216151952,'ordinal expression',
|
|
GetTypeDesc(CaseExprResolved.TypeEl),CaseOf.CaseExpr);
|
|
|
|
for i:=0 to CaseOf.Elements.Count-1 do
|
|
begin
|
|
El:=TPasElement(CaseOf.Elements[i]);
|
|
if El.ClassType=TPasImplCaseStatement then
|
|
begin
|
|
Stat:=TPasImplCaseStatement(El);
|
|
for j:=0 to Stat.Expressions.Count-1 do
|
|
begin
|
|
//writeln('TPasResolver.ResolveImplCaseOf Stat.Expr[',j,']=',GetObjName(El));
|
|
OfExpr:=TPasExpr(Stat.Expressions[j]);
|
|
ResolveExpr(OfExpr,rraRead);
|
|
ComputeElement(OfExpr,OfExprResolved,[rcConstant]);
|
|
if OfExprResolved.BaseType=btRange then
|
|
ConvertRangeToFirstValue(OfExprResolved);
|
|
CheckEqualResCompatibility(CaseExprResolved,OfExprResolved,OfExpr,true);
|
|
end;
|
|
ResolveImplElement(Stat.Body);
|
|
end
|
|
else if El.ClassType=TPasImplCaseElse then
|
|
ResolveImplBlock(TPasImplCaseElse(El))
|
|
else
|
|
RaiseNotYetImplemented(20160922163448,El);
|
|
end;
|
|
// Note: CaseOf.ElseBranch was already resolved via Elements
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveImplLabelMark(Mark: TPasImplLabelMark);
|
|
begin
|
|
RaiseNotYetImplemented(20161014141636,Mark);
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveImplForLoop(Loop: TPasImplForLoop);
|
|
var
|
|
VarResolved, StartResolved, EndResolved: TPasResolverResult;
|
|
begin
|
|
// loop var
|
|
ResolveExpr(Loop.VariableName,rraAssign);
|
|
ComputeElement(Loop.VariableName,VarResolved,[rcNoImplicitProc]);
|
|
if ResolvedElCanBeVarParam(VarResolved)
|
|
and ((VarResolved.BaseType in (btAllBooleans+btAllInteger+[btChar,btWideChar]))
|
|
or ((VarResolved.BaseType=btContext) and (VarResolved.TypeEl.ClassType=TPasEnumType))) then
|
|
else
|
|
RaiseMsg(20170216151955,nVariableIdentifierExpected,sVariableIdentifierExpected,[],Loop.VariableName);
|
|
|
|
// start value
|
|
ResolveExpr(Loop.StartExpr,rraRead);
|
|
ComputeElement(Loop.StartExpr,StartResolved,[]);
|
|
if CheckAssignResCompatibility(VarResolved,StartResolved,Loop.StartExpr,true)=cIncompatible then
|
|
RaiseIncompatibleTypeRes(20170216151958,nIncompatibleTypesGotExpected,
|
|
[],StartResolved,VarResolved,Loop.StartExpr);
|
|
|
|
// end value
|
|
ResolveExpr(Loop.EndExpr,rraRead);
|
|
ComputeElement(Loop.EndExpr,EndResolved,[]);
|
|
if CheckAssignResCompatibility(VarResolved,EndResolved,Loop.EndExpr,false)=cIncompatible then
|
|
RaiseIncompatibleTypeRes(20170216152001,nIncompatibleTypesGotExpected,
|
|
[],EndResolved,VarResolved,Loop.EndExpr);
|
|
|
|
ResolveImplElement(Loop.Body);
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveImplWithDo(El: TPasImplWithDo);
|
|
var
|
|
i, OldScopeCount: Integer;
|
|
Expr, ErrorEl: TPasExpr;
|
|
ExprResolved: TPasResolverResult;
|
|
TypeEl: TPasType;
|
|
WithScope: TPasWithScope;
|
|
WithExprScope: TPasWithExprScope;
|
|
ExprScope: TPasScope;
|
|
OnlyTypeMembers: Boolean;
|
|
ClassEl: TPasClassType;
|
|
begin
|
|
OldScopeCount:=ScopeCount;
|
|
WithScope:=TPasWithScope(CreateScope(El,TPasWithScope));
|
|
PushScope(WithScope);
|
|
for i:=0 to El.Expressions.Count-1 do
|
|
begin
|
|
Expr:=TPasExpr(El.Expressions[i]);
|
|
ResolveExpr(Expr,rraRead);
|
|
ComputeElement(Expr,ExprResolved,[rcSkipTypeAlias]);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveImplWithDo ExprResolved=',GetResolverResultDesc(ExprResolved));
|
|
{$ENDIF}
|
|
ErrorEl:=Expr;
|
|
TypeEl:=ExprResolved.TypeEl;
|
|
// ToDo: use last element in Expr for error position
|
|
if TypeEl=nil then
|
|
RaiseMsg(20170216152004,nExprTypeMustBeClassOrRecordTypeGot,sExprTypeMustBeClassOrRecordTypeGot,
|
|
[BaseTypeNames[ExprResolved.BaseType]],ErrorEl);
|
|
|
|
OnlyTypeMembers:=false;
|
|
if TypeEl.ClassType=TPasRecordType then
|
|
begin
|
|
ExprScope:=TPasRecordType(TypeEl).CustomData as TPasRecordScope;
|
|
if ExprResolved.IdentEl is TPasType then
|
|
// e.g. with TPoint do PointInCircle
|
|
OnlyTypeMembers:=true;
|
|
end
|
|
else if TypeEl.ClassType=TPasClassType then
|
|
begin
|
|
ExprScope:=TPasClassType(TypeEl).CustomData as TPasClassScope;
|
|
if ExprResolved.IdentEl is TPasType then
|
|
// e.g. with TFPMemoryImage do FindHandlerFromExtension()
|
|
OnlyTypeMembers:=true;
|
|
end
|
|
else if TypeEl.ClassType=TPasClassOfType then
|
|
begin
|
|
// e.g. with ImageClass do FindHandlerFromExtension()
|
|
ClassEl:=ResolveAliasType(TPasClassOfType(TypeEl).DestType) as TPasClassType;
|
|
ExprScope:=ClassEl.CustomData as TPasClassScope;
|
|
OnlyTypeMembers:=true;
|
|
end
|
|
else
|
|
RaiseMsg(20170216152007,nExprTypeMustBeClassOrRecordTypeGot,sExprTypeMustBeClassOrRecordTypeGot,
|
|
[TypeEl.ElementTypeName],ErrorEl);
|
|
WithExprScope:=ScopeClass_WithExpr.Create;
|
|
WithExprScope.WithScope:=WithScope;
|
|
WithExprScope.Index:=i;
|
|
WithExprScope.Expr:=Expr;
|
|
WithExprScope.Scope:=ExprScope;
|
|
if ExprResolved.IdentEl is TPasType then
|
|
Include(WithExprScope.flags,wesfNeedTmpVar);
|
|
if OnlyTypeMembers then
|
|
Include(WithExprScope.flags,wesfOnlyTypeMembers);
|
|
if (not (rrfWritable in ExprResolved.Flags))
|
|
and (ExprResolved.BaseType=btContext)
|
|
and (ExprResolved.TypeEl.ClassType=TPasRecordType) then
|
|
Include(WithExprScope.flags,wesfConstParent);
|
|
WithScope.ExpressionScopes.Add(WithExprScope);
|
|
PushScope(WithExprScope);
|
|
end;
|
|
ResolveImplElement(El.Body);
|
|
CheckTopScope(ScopeClass_WithExpr);
|
|
if TopScope<>WithScope.ExpressionScopes[WithScope.ExpressionScopes.Count-1] then
|
|
RaiseInternalError(20160923102846);
|
|
while ScopeCount>OldScopeCount do
|
|
PopScope;
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveImplAsm(El: TPasImplAsmStatement);
|
|
begin
|
|
if El=nil then ;
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveImplAssign(El: TPasImplAssign);
|
|
var
|
|
LeftResolved, RightResolved: TPasResolverResult;
|
|
Flags: TPasResolverComputeFlags;
|
|
Access: TResolvedRefAccess;
|
|
begin
|
|
if El.Kind=akDefault then
|
|
Access:=rraAssign
|
|
else
|
|
Access:=rraReadAndAssign;
|
|
ResolveExpr(El.left,Access);
|
|
ResolveExpr(El.right,rraRead);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveImplAssign Kind=',El.Kind,' left=',GetObjName(El.left),' right=',GetObjName(el.right));
|
|
{$ENDIF}
|
|
// check LHS can be assigned
|
|
ComputeElement(El.left,LeftResolved,[rcSkipTypeAlias,rcNoImplicitProc]);
|
|
CheckCanBeLHS(LeftResolved,true,El.left);
|
|
// compute RHS
|
|
Flags:=[rcSkipTypeAlias];
|
|
if IsProcedureType(LeftResolved,true) then
|
|
if (msDelphi in CurrentParser.CurrentModeswitches) then
|
|
Include(Flags,rcNoImplicitProc) // a proc type can use param less procs
|
|
else
|
|
Include(Flags,rcNoImplicitProcType); // a proc type can use a param less proc type
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveImplAssign Left=',GetResolverResultDesc(LeftResolved),' Flags=',dbgs(Flags));
|
|
{$ENDIF}
|
|
ComputeElement(El.right,RightResolved,Flags);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveImplAssign Right=',GetResolverResultDesc(RightResolved));
|
|
{$ENDIF}
|
|
|
|
case El.Kind of
|
|
akDefault:
|
|
CheckAssignResCompatibility(LeftResolved,RightResolved,El.right,true);
|
|
akAdd, akMinus,akMul,akDivision:
|
|
begin
|
|
if (El.Kind in [akAdd,akMinus,akMul]) and (LeftResolved.BaseType in btAllInteger) then
|
|
begin
|
|
if (not (rrfReadable in RightResolved.Flags))
|
|
or not (RightResolved.BaseType in btAllInteger) then
|
|
RaiseMsg(20170216152009,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected,
|
|
[BaseTypes[RightResolved.BaseType],BaseTypes[LeftResolved.BaseType]],El.right);
|
|
end
|
|
else if (El.Kind=akAdd) and (LeftResolved.BaseType in btAllStrings) then
|
|
begin
|
|
if (not (rrfReadable in RightResolved.Flags))
|
|
or not (RightResolved.BaseType in btAllStringAndChars) then
|
|
RaiseMsg(20170216152012,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected,
|
|
[BaseTypes[RightResolved.BaseType],BaseTypes[LeftResolved.BaseType]],El.right);
|
|
end
|
|
else if (El.Kind in [akAdd,akMinus,akMul,akDivision])
|
|
and (LeftResolved.BaseType in btAllFloats) then
|
|
begin
|
|
if (not (rrfReadable in RightResolved.Flags))
|
|
or not (RightResolved.BaseType in (btAllInteger+btAllFloats)) then
|
|
RaiseMsg(20170216152107,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected,
|
|
[BaseTypes[RightResolved.BaseType],BaseTypes[LeftResolved.BaseType]],El.right);
|
|
end
|
|
else if (LeftResolved.BaseType=btSet) and (El.Kind in [akAdd,akMinus,akMul]) then
|
|
begin
|
|
if (not (rrfReadable in RightResolved.Flags))
|
|
or not (RightResolved.BaseType=btSet) then
|
|
RaiseMsg(20170216152110,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected,
|
|
[BaseTypeNames[RightResolved.BaseType],'set of '+BaseTypeNames[LeftResolved.SubType]],El.right);
|
|
if (LeftResolved.SubType=RightResolved.SubType)
|
|
or ((LeftResolved.SubType in btAllInteger) and (RightResolved.SubType in btAllInteger))
|
|
or ((LeftResolved.SubType in btAllBooleans) and (RightResolved.SubType in btAllBooleans))
|
|
then
|
|
else
|
|
RaiseMsg(20170216152117,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected,
|
|
['set of '+BaseTypeNames[RightResolved.SubType],'set of '+BaseTypeNames[LeftResolved.SubType]],El.right);
|
|
end
|
|
else
|
|
RaiseMsg(20170216152125,nIllegalQualifier,sIllegalQualifier,[AssignKindNames[El.Kind]],El);
|
|
end;
|
|
else
|
|
RaiseNotYetImplemented(20160927143649,El,'AssignKind '+AssignKindNames[El.Kind]);
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveImplSimple(El: TPasImplSimple);
|
|
var
|
|
ExprResolved: TPasResolverResult;
|
|
Expr: TPasExpr;
|
|
begin
|
|
Expr:=El.expr;
|
|
ResolveExpr(Expr,rraRead);
|
|
ComputeElement(Expr,ExprResolved,[rcSkipTypeAlias]);
|
|
if (rrfCanBeStatement in ExprResolved.Flags) then
|
|
exit;
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveImplSimple El=',GetObjName(El),' El.Expr=',GetObjName(El.Expr),' ExprResolved=',GetResolverResultDesc(ExprResolved));
|
|
{$ENDIF}
|
|
RaiseMsg(20170216152127,nIllegalExpression,sIllegalExpression,[],El);
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveImplRaise(El: TPasImplRaise);
|
|
var
|
|
ResolvedEl: TPasResolverResult;
|
|
begin
|
|
if El.ExceptObject<>nil then
|
|
begin
|
|
ResolveExpr(El.ExceptObject,rraRead);
|
|
ComputeElement(El.ExceptObject,ResolvedEl,[rcSkipTypeAlias]);
|
|
CheckIsClass(El.ExceptObject,ResolvedEl);
|
|
if ResolvedEl.IdentEl<>nil then
|
|
begin
|
|
if (ResolvedEl.IdentEl is TPasVariable)
|
|
or (ResolvedEl.IdentEl is TPasArgument) then
|
|
else
|
|
RaiseMsg(20170216152133,nXExpectedButYFound,sXExpectedButYFound,
|
|
['variable',ResolvedEl.IdentEl.ElementTypeName],El.ExceptObject);
|
|
end
|
|
else if ResolvedEl.ExprEl<>nil then
|
|
else
|
|
RaiseMsg(201702303145230,nXExpectedButYFound,sXExpectedButYFound,
|
|
['variable',GetResolverResultDesc(ResolvedEl)],El.ExceptObject);
|
|
if not (rrfReadable in ResolvedEl.Flags) then
|
|
RaiseMsg(20170303145037,nNotReadable,sNotReadable,[],El.ExceptObject);
|
|
end;
|
|
if El.ExceptAddr<>nil then
|
|
ResolveExpr(El.ExceptAddr,rraRead);
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveExpr(El: TPasExpr; Access: TResolvedRefAccess);
|
|
var
|
|
Primitive: TPrimitiveExpr;
|
|
ElClass: TClass;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveExpr ',GetObjName(El));
|
|
{$ENDIF}
|
|
if El=nil then
|
|
RaiseNotYetImplemented(20160922163453,El);
|
|
ElClass:=El.ClassType;
|
|
if ElClass=TPrimitiveExpr then
|
|
begin
|
|
Primitive:=TPrimitiveExpr(El);
|
|
case Primitive.Kind of
|
|
pekIdent: ResolveNameExpr(El,Primitive.Value,Access);
|
|
pekNumber: ;
|
|
pekString: ;
|
|
pekNil,pekBoolConst: ;
|
|
else
|
|
RaiseNotYetImplemented(20160922163451,El);
|
|
end;
|
|
end
|
|
else if ElClass=TUnaryExpr then
|
|
ResolveExpr(TUnaryExpr(El).Operand,Access)
|
|
else if ElClass=TBinaryExpr then
|
|
ResolveBinaryExpr(TBinaryExpr(El),Access)
|
|
else if ElClass=TParamsExpr then
|
|
ResolveParamsExpr(TParamsExpr(El),Access)
|
|
else if ElClass=TBoolConstExpr then
|
|
else if ElClass=TNilExpr then
|
|
else if ElClass=TSelfExpr then
|
|
ResolveNameExpr(El,'Self',Access)
|
|
else if ElClass=TInheritedExpr then
|
|
ResolveInherited(TInheritedExpr(El),Access)
|
|
else if ElClass=TArrayValues then
|
|
begin
|
|
if Access<>rraRead then
|
|
RaiseMsg(20170303205743,nVariableIdentifierExpected,sVariableIdentifierExpected,
|
|
[],El);
|
|
ResolveArrayValues(TArrayValues(El));
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20170222184329,El);
|
|
|
|
if El.format1<>nil then
|
|
ResolveExpr(El.format1,rraRead);
|
|
if El.format2<>nil then
|
|
ResolveExpr(El.format2,rraRead);
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveStatementConditionExpr(El: TPasExpr);
|
|
var
|
|
ResolvedCond: TPasResolverResult;
|
|
begin
|
|
ResolveExpr(El,rraRead);
|
|
ComputeElement(El,ResolvedCond,[rcSkipTypeAlias]);
|
|
if ResolvedCond.BaseType<>btBoolean then
|
|
RaiseMsg(20170216152135,nXExpectedButYFound,sXExpectedButYFound,
|
|
[BaseTypeNames[btBoolean],BaseTypeNames[ResolvedCond.BaseType]],El);
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveNameExpr(El: TPasExpr; const aName: string;
|
|
Access: TResolvedRefAccess);
|
|
var
|
|
FindData: TPRFindData;
|
|
DeclEl: TPasElement;
|
|
Proc: TPasProcedure;
|
|
Ref: TResolvedReference;
|
|
BuiltInProc: TResElDataBuiltInProc;
|
|
begin
|
|
DeclEl:=FindElementWithoutParams(aName,FindData,El,false);
|
|
Ref:=CreateReference(DeclEl,El,Access,@FindData);
|
|
CheckFoundElement(FindData,Ref);
|
|
if DeclEl is TPasProcedure then
|
|
begin
|
|
// identifier is a proc and args brackets are missing
|
|
if El.Parent.ClassType=TPasProperty then
|
|
// a property accessor does not need args -> ok
|
|
else
|
|
begin
|
|
// examples: funca or @proca or a.funca or @a.funca ...
|
|
Proc:=TPasProcedure(DeclEl);
|
|
if ProcNeedsParams(Proc.ProcType) and not ExprIsAddrTarget(El) then
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveNameExpr ',GetObjName(El));
|
|
{$ENDIF}
|
|
RaiseMsg(20170216152138,nWrongNumberOfParametersForCallTo,
|
|
sWrongNumberOfParametersForCallTo,[Proc.Name],El);
|
|
end;
|
|
end;
|
|
end
|
|
else if DeclEl.ClassType=TPasUnresolvedSymbolRef then
|
|
begin
|
|
if DeclEl.CustomData is TResElDataBuiltInProc then
|
|
begin
|
|
BuiltInProc:=TResElDataBuiltInProc(DeclEl.CustomData);
|
|
BuiltInProc.GetCallCompatibility(BuiltInProc,El,true);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveInherited(El: TInheritedExpr;
|
|
Access: TResolvedRefAccess);
|
|
var
|
|
ProcScope, DeclProcScope: TPasProcedureScope;
|
|
AncestorScope: TPasClassScope;
|
|
DeclProc, AncestorProc: TPasProcedure;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveInherited El.Parent=',GetTreeDesc(El.Parent));
|
|
{$ENDIF}
|
|
if (El.Parent.ClassType=TBinaryExpr)
|
|
and (TBinaryExpr(El.Parent).OpCode=eopNone) then
|
|
begin
|
|
// e.g. 'inherited Proc;'
|
|
ResolveInheritedCall(TBinaryExpr(El.Parent),Access);
|
|
exit;
|
|
end;
|
|
|
|
// 'inherited;' without expression
|
|
CheckTopScope(TPasProcedureScope);
|
|
ProcScope:=TPasProcedureScope(TopScope);
|
|
if ProcScope.ClassScope=nil then
|
|
RaiseMsg(20170216152141,nInheritedOnlyWorksInMethods,sInheritedOnlyWorksInMethods,[],El);
|
|
|
|
AncestorScope:=ProcScope.ClassScope.AncestorScope;
|
|
if AncestorScope=nil then
|
|
begin
|
|
// 'inherited;' without ancestor class is silently ignored
|
|
exit;
|
|
end;
|
|
|
|
// search ancestor in element, i.e. 'inherited' expression
|
|
DeclProc:=ProcScope.DeclarationProc;
|
|
DeclProcScope:=DeclProc.CustomData as TPasProcedureScope;
|
|
AncestorProc:=DeclProcScope.OverriddenProc;
|
|
if AncestorProc<>nil then
|
|
begin
|
|
CreateReference(AncestorProc,El,Access);
|
|
if AncestorProc.IsAbstract then
|
|
RaiseMsg(20170216152144,nAbstractMethodsCannotBeCalledDirectly,
|
|
sAbstractMethodsCannotBeCalledDirectly,[],El);
|
|
end
|
|
else
|
|
begin
|
|
// 'inherited;' without ancestor method is silently ignored
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveInheritedCall(El: TBinaryExpr;
|
|
Access: TResolvedRefAccess);
|
|
// El.OpCode=eopNone
|
|
// El.left is TInheritedExpr
|
|
// El.right is the identifier and parameters
|
|
var
|
|
ProcScope: TPasProcedureScope;
|
|
AncestorScope: TPasClassScope;
|
|
AncestorClass: TPasClassType;
|
|
InhScope: TPasDotClassScope;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveInheritedCall El=',GetTreeDesc(El));
|
|
{$ENDIF}
|
|
|
|
CheckTopScope(TPasProcedureScope);
|
|
ProcScope:=TPasProcedureScope(TopScope);
|
|
if ProcScope.ClassScope=nil then
|
|
RaiseMsg(20170216152148,nInheritedOnlyWorksInMethods,sInheritedOnlyWorksInMethods,[],El);
|
|
|
|
AncestorScope:=ProcScope.ClassScope.AncestorScope;
|
|
if AncestorScope=nil then
|
|
RaiseMsg(20170216152151,nInheritedNeedsAncestor,sInheritedNeedsAncestor,[],El.left);
|
|
|
|
// search call in ancestor
|
|
AncestorClass:=TPasClassType(AncestorScope.Element);
|
|
InhScope:=PushClassDotScope(AncestorClass);
|
|
InhScope.InheritedExpr:=true;
|
|
ResolveExpr(El.right,Access);
|
|
PopScope;
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveBinaryExpr(El: TBinaryExpr;
|
|
Access: TResolvedRefAccess);
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
//writeln('TPasResolver.ResolveBinaryExpr left=',GetObjName(El.left),' right=',GetObjName(El.right),' opcode=',OpcodeStrings[El.OpCode]);
|
|
{$ENDIF}
|
|
ResolveExpr(El.left,rraRead);
|
|
if El.right=nil then exit;
|
|
case El.OpCode of
|
|
eopNone:
|
|
case El.Kind of
|
|
pekRange:
|
|
ResolveExpr(El.right,rraRead);
|
|
else
|
|
if El.left.ClassType=TInheritedExpr then
|
|
else
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveBinaryExpr El.Kind=',ExprKindNames[El.Kind],' El.Left=',GetObjName(El.left),' El.Right=',GetObjName(El.right),' parent=',GetObjName(El.Parent));
|
|
{$ENDIF}
|
|
RaiseNotYetImplemented(20160922163456,El);
|
|
end;
|
|
end;
|
|
eopAdd,
|
|
eopSubtract,
|
|
eopMultiply,
|
|
eopDivide,
|
|
eopDiv,
|
|
eopMod,
|
|
eopPower,
|
|
eopShr,
|
|
eopShl,
|
|
eopNot,
|
|
eopAnd,
|
|
eopOr,
|
|
eopXor,
|
|
eopEqual,
|
|
eopNotEqual,
|
|
eopLessThan,
|
|
eopGreaterThan,
|
|
eopLessthanEqual,
|
|
eopGreaterThanEqual,
|
|
eopIn,
|
|
eopIs,
|
|
eopAs,
|
|
eopSymmetricaldifference:
|
|
ResolveExpr(El.right,rraRead);
|
|
eopSubIdent:
|
|
ResolveSubIdent(El,Access);
|
|
else
|
|
RaiseNotYetImplemented(20160922163459,El,OpcodeStrings[El.OpCode]);
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveSubIdent(El: TBinaryExpr;
|
|
Access: TResolvedRefAccess);
|
|
var
|
|
aModule: TPasModule;
|
|
ClassEl: TPasClassType;
|
|
ClassScope: TPasDotClassScope;
|
|
LeftResolved: TPasResolverResult;
|
|
Left: TPasExpr;
|
|
RecordEl: TPasRecordType;
|
|
RecordScope: TPasDotRecordScope;
|
|
begin
|
|
Left:=El.left;
|
|
//writeln('TPasResolver.ResolveSubIdent Left=',GetObjName(Left));
|
|
ComputeElement(Left,LeftResolved,[]);
|
|
|
|
if LeftResolved.BaseType=btModule then
|
|
begin
|
|
// e.g. unitname.identifier
|
|
// => search in interface and if this is our module in the implementation
|
|
aModule:=LeftResolved.IdentEl as TPasModule;
|
|
PushModuleDotScope(aModule);
|
|
ResolveExpr(El.right,Access);
|
|
PopScope;
|
|
exit;
|
|
end
|
|
else if LeftResolved.TypeEl=nil then
|
|
begin
|
|
// illegal qualifier, see below
|
|
end
|
|
else if LeftResolved.TypeEl.ClassType=TPasClassType then
|
|
begin
|
|
ClassEl:=TPasClassType(LeftResolved.TypeEl);
|
|
ClassScope:=PushClassDotScope(ClassEl);
|
|
if LeftResolved.IdentEl is TPasType then
|
|
// e.g. TFPMemoryImage.FindHandlerFromExtension()
|
|
ClassScope.OnlyTypeMembers:=true
|
|
else
|
|
// e.g. Image.Width
|
|
ClassScope.OnlyTypeMembers:=false;
|
|
ResolveExpr(El.right,Access);
|
|
PopScope;
|
|
exit;
|
|
end
|
|
else if LeftResolved.TypeEl.ClassType=TPasClassOfType then
|
|
begin
|
|
// e.g. ImageClass.FindHandlerFromExtension()
|
|
ClassEl:=ResolveAliasType(TPasClassOfType(LeftResolved.TypeEl).DestType) as TPasClassType;
|
|
ClassScope:=PushClassDotScope(ClassEl);
|
|
ClassScope.OnlyTypeMembers:=true;
|
|
ResolveExpr(El.right,Access);
|
|
PopScope;
|
|
exit;
|
|
end
|
|
else if LeftResolved.TypeEl.ClassType=TPasRecordType then
|
|
begin
|
|
RecordEl:=TPasRecordType(LeftResolved.TypeEl);
|
|
RecordScope:=PushRecordDotScope(RecordEl);
|
|
RecordScope.ConstParent:=not (rrfWritable in LeftResolved.Flags);
|
|
if LeftResolved.IdentEl is TPasType then
|
|
// e.g. TPoint.PointInCircle
|
|
RecordScope.OnlyTypeMembers:=true
|
|
else
|
|
begin
|
|
// e.g. aPoint.X
|
|
AccessExpr(El.left,Access);
|
|
RecordScope.OnlyTypeMembers:=false;
|
|
end;
|
|
ResolveExpr(El.right,Access);
|
|
PopScope;
|
|
exit;
|
|
end
|
|
else if LeftResolved.TypeEl.ClassType=TPasEnumType then
|
|
begin
|
|
if LeftResolved.IdentEl is TPasType then
|
|
begin
|
|
// e.g. TShiftState.ssAlt
|
|
PushEnumDotScope(TPasEnumType(LeftResolved.TypeEl));
|
|
ResolveExpr(El.right,Access);
|
|
PopScope;
|
|
exit;
|
|
end;
|
|
end
|
|
else
|
|
RaiseMsg(20170216152541,nExprTypeMustBeClassOrRecordTypeGot,sExprTypeMustBeClassOrRecordTypeGot,
|
|
[LeftResolved.TypeEl.ElementTypeName],El);
|
|
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveSubIdent left=',GetObjName(Left),' right=',GetObjName(El.right),' leftresolved=',GetResolverResultDesc(LeftResolved));
|
|
{$ENDIF}
|
|
RaiseMsg(20170216152157,nIllegalQualifier,sIllegalQualifier,['.'],El);
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveParamsExpr(Params: TParamsExpr;
|
|
Access: TResolvedRefAccess);
|
|
var
|
|
i, ScopeDepth: Integer;
|
|
ParamAccess: TResolvedRefAccess;
|
|
begin
|
|
if (Params.Kind=pekSet) and not (Access in [rraRead,rraParamToUnknownProc]) then
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveParamsExpr SET literal Access=',Access);
|
|
{$ENDIF}
|
|
RaiseMsg(20170303211052,nVariableIdentifierExpected,sVariableIdentifierExpected,[],Params);
|
|
end;
|
|
|
|
// first resolve params
|
|
ResetSubScopes(ScopeDepth);
|
|
if Params.Kind in [pekFuncParams,pekArrayParams] then
|
|
ParamAccess:=rraParamToUnknownProc
|
|
else
|
|
ParamAccess:=rraRead;
|
|
for i:=0 to length(Params.Params)-1 do
|
|
ResolveExpr(Params.Params[i],ParamAccess);
|
|
RestoreSubScopes(ScopeDepth);
|
|
|
|
// then resolve the call, typecast, array, set
|
|
if (Params.Kind=pekFuncParams) then
|
|
ResolveFuncParamsExpr(Params,Access)
|
|
else if (Params.Kind=pekArrayParams) then
|
|
ResolveArrayParamsExpr(Params,Access)
|
|
else if (Params.Kind=pekSet) then
|
|
ResolveSetParamsExpr(Params)
|
|
else
|
|
RaiseNotYetImplemented(20160922163501,Params);
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveFuncParamsExpr(Params: TParamsExpr;
|
|
Access: TResolvedRefAccess);
|
|
|
|
procedure FinishProcParams(ProcType: TPasProcedureType);
|
|
var
|
|
ParamAccess: TResolvedRefAccess;
|
|
i: Integer;
|
|
begin
|
|
if not (Access in [rraRead,rraParamToUnknownProc]) then
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveFuncParamsExpr.FinishProcParams Params=',GetObjName(Params),' Value=',GetObjName(Params.Value),' Access=',Access);
|
|
{$ENDIF}
|
|
RaiseMsg(20170306104440,nVariableIdentifierExpected,sVariableIdentifierExpected,[],Params);
|
|
end;
|
|
for i:=0 to length(Params.Params)-1 do
|
|
begin
|
|
ParamAccess:=rraRead;
|
|
if i<ProcType.Args.Count then
|
|
case TPasArgument(ProcType.Args[i]).Access of
|
|
argVar: ParamAccess:=rraVarParam;
|
|
argOut: ParamAccess:=rraOutParam;
|
|
end;
|
|
AccessExpr(Params.Params[i],ParamAccess);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
ElName, Msg: String;
|
|
FindCallData: TFindCallElData;
|
|
Abort: boolean;
|
|
El, FoundEl: TPasElement;
|
|
Ref: TResolvedReference;
|
|
FindData: TPRFindData;
|
|
BuiltInProc: TResElDataBuiltInProc;
|
|
SubParams: TParamsExpr;
|
|
ResolvedEl: TPasResolverResult;
|
|
Value: TPasExpr;
|
|
TypeEl: TPasType;
|
|
C: TClass;
|
|
begin
|
|
Value:=Params.Value;
|
|
if IsNameExpr(Value) then
|
|
begin
|
|
// e.g. Name() -> find compatible
|
|
if Value.ClassType=TPrimitiveExpr then
|
|
ElName:=TPrimitiveExpr(Value).Value
|
|
else
|
|
ElName:='Self';
|
|
FindCallData:=Default(TFindCallElData);
|
|
FindCallData.Params:=Params;
|
|
Abort:=false;
|
|
IterateElements(ElName,@OnFindCallElements,@FindCallData,Abort);
|
|
if FindCallData.Found=nil then
|
|
RaiseIdentifierNotFound(20170216152544,ElName,Value);
|
|
if FindCallData.Distance=cIncompatible then
|
|
begin
|
|
// FoundEl one element, but it was incompatible => raise error
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveFuncParamsExpr found one element, but it was incompatible => check again to raise error. Found=',GetObjName(FindCallData.Found));
|
|
{$ENDIF}
|
|
if FindCallData.Found is TPasProcedure then
|
|
CheckCallProcCompatibility(TPasProcedure(FindCallData.Found).ProcType,Params,true)
|
|
else if FindCallData.Found is TPasProcedureType then
|
|
CheckTypeCast(TPasProcedureType(FindCallData.Found),Params,true)
|
|
else if FindCallData.Found.ClassType=TPasUnresolvedSymbolRef then
|
|
begin
|
|
if FindCallData.Found.CustomData is TResElDataBuiltInProc then
|
|
begin
|
|
BuiltInProc:=TResElDataBuiltInProc(FindCallData.Found.CustomData);
|
|
BuiltInProc.GetCallCompatibility(BuiltInProc,Params,true);
|
|
end
|
|
else if FindCallData.Found.CustomData is TResElDataBaseType then
|
|
CheckTypeCast(TPasUnresolvedSymbolRef(FindCallData.Found),Params,true)
|
|
else
|
|
RaiseNotYetImplemented(20161006132825,FindCallData.Found);
|
|
end
|
|
else if FindCallData.Found is TPasType then
|
|
// Note: check TPasType after TPasUnresolvedSymbolRef
|
|
CheckTypeCast(TPasType(FindCallData.Found),Params,true)
|
|
else if FindCallData.Found is TPasVariable then
|
|
begin
|
|
TypeEl:=ResolveAliasType(TPasVariable(FindCallData.Found).VarType);
|
|
if TypeEl is TPasProcedureType then
|
|
CheckCallProcCompatibility(TPasProcedureType(TypeEl),Params,true)
|
|
else
|
|
RaiseMsg(20170405003522,nIllegalQualifier,sIllegalQualifier,['('],Params);
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20161003134755,FindCallData.Found);
|
|
end;
|
|
if FindCallData.Count>1 then
|
|
begin
|
|
// multiple overloads fit => search again and list the candidates
|
|
FindCallData:=Default(TFindCallElData);
|
|
FindCallData.Params:=Params;
|
|
FindCallData.List:=TFPList.Create;
|
|
try
|
|
IterateElements(ElName,@OnFindCallElements,@FindCallData,Abort);
|
|
Msg:='';
|
|
for i:=0 to FindCallData.List.Count-1 do
|
|
begin
|
|
// ToDo: create a hint for each candidate
|
|
El:=TPasElement(FindCallData.List[i]);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveFuncParamsExpr Overload Candidate: ',GetElementSourcePosStr(El),' ',GetTreeDesc(El));
|
|
{$ENDIF}
|
|
Msg:=Msg+', ';
|
|
Msg:=Msg+GetElementSourcePosStr(El);
|
|
end;
|
|
RaiseMsg(20170216152200,nCantDetermineWhichOverloadedFunctionToCall,
|
|
sCantDetermineWhichOverloadedFunctionToCall+Msg,[ElName],Value);
|
|
finally
|
|
FindCallData.List.Free;
|
|
end;
|
|
end;
|
|
|
|
// FoundEl compatible element -> create reference
|
|
FoundEl:=FindCallData.Found;
|
|
Ref:=CreateReference(FoundEl,Value,rraRead);
|
|
if FindCallData.StartScope.ClassType=ScopeClass_WithExpr then
|
|
Ref.WithExprScope:=TPasWithExprScope(FindCallData.StartScope);
|
|
FindData:=Default(TPRFindData);
|
|
FindData.ErrorPosEl:=Value;
|
|
FindData.StartScope:=FindCallData.StartScope;
|
|
FindData.ElScope:=FindCallData.ElScope;
|
|
FindData.Found:=FoundEl;
|
|
CheckFoundElement(FindData,Ref);
|
|
|
|
// set param expression Access flags
|
|
if FoundEl is TPasProcedure then
|
|
// call proc
|
|
FinishProcParams(TPasProcedure(FoundEl).ProcType)
|
|
else if FoundEl is TPasType then
|
|
begin
|
|
TypeEl:=ResolveAliasType(TPasType(FoundEl));
|
|
C:=TypeEl.ClassType;
|
|
if (C=TPasClassType)
|
|
or (C=TPasClassOfType)
|
|
or (C=TPasEnumType)
|
|
or (C=TPasSetType)
|
|
or (C=TPasPointerType)
|
|
or (C=TPasProcedureType)
|
|
or (C=TPasFunctionType)
|
|
or (C=TPasArrayType) then
|
|
begin
|
|
// type cast
|
|
for i:=0 to length(Params.Params)-1 do
|
|
AccessExpr(Params.Params[i],Access);
|
|
end
|
|
else if C=TPasUnresolvedSymbolRef then
|
|
begin
|
|
if TypeEl.CustomData is TResElDataBuiltInProc then
|
|
begin
|
|
// call built-in proc
|
|
BuiltInProc:=TResElDataBuiltInProc(TypeEl.CustomData);
|
|
if Assigned(BuiltInProc.FinishParamsExpression) then
|
|
BuiltInProc.FinishParamsExpression(BuiltInProc,Params)
|
|
else
|
|
for i:=0 to length(Params.Params)-1 do
|
|
AccessExpr(Params.Params[i],rraRead);
|
|
end
|
|
else if TypeEl.CustomData is TResElDataBaseType then
|
|
begin
|
|
// type cast to base type
|
|
for i:=0 to length(Params.Params)-1 do
|
|
AccessExpr(Params.Params[i],Access);
|
|
end
|
|
else
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveFuncParamsExpr FoundEl=',GetObjName(FoundEl),' CustomData=',GetObjName(FoundEl.CustomData));
|
|
{$ENDIF}
|
|
RaiseNotYetImplemented(20170325145720,Params);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveFuncParamsExpr FoundEl=',GetObjName(FoundEl),' CustomData=',GetObjName(FoundEl.CustomData));
|
|
{$ENDIF}
|
|
RaiseMsg(20170306121908,nIllegalExpression,sIllegalExpression,[],Params);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
// FoundEl is not a type, maybe a var
|
|
ComputeElement(FoundEl,ResolvedEl,[rcNoImplicitProc]);
|
|
if ResolvedEl.TypeEl is TPasProcedureType then
|
|
begin
|
|
FinishProcParams(TPasProcedureType(ResolvedEl.TypeEl));
|
|
exit;
|
|
end;
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveFuncParamsExpr FoundEl=',GetObjName(FoundEl),' CustomData=',GetObjName(FoundEl.CustomData),' Resolvedel=',GetResolverResultDesc(ResolvedEl));
|
|
{$ENDIF}
|
|
RaiseMsg(20170306104301,nIllegalExpression,sIllegalExpression,[],Params);
|
|
end;
|
|
end
|
|
else if Value.ClassType=TParamsExpr then
|
|
begin
|
|
SubParams:=TParamsExpr(Value);
|
|
if (SubParams.Kind in [pekArrayParams,pekFuncParams]) then
|
|
begin
|
|
// e.g. Name()() or Name[]()
|
|
ResolveExpr(SubParams,rraRead);
|
|
ComputeElement(SubParams,ResolvedEl,[rcNoImplicitProc]);
|
|
if IsProcedureType(ResolvedEl,true) then
|
|
begin
|
|
CheckCallProcCompatibility(TPasProcedureType(ResolvedEl.TypeEl),Params,true);
|
|
CreateReference(TPasProcedureType(ResolvedEl.TypeEl),Value,Access);
|
|
exit;
|
|
end
|
|
end;
|
|
RaiseMsg(20170216152202,nIllegalQualifier,sIllegalQualifier,['('],Params);
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20161014085118,Params.Value);
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveArrayParamsExpr(Params: TParamsExpr;
|
|
Access: TResolvedRefAccess);
|
|
var
|
|
ResolvedEl: TPasResolverResult;
|
|
|
|
procedure ResolveValueName(Value: TPasElement; ArrayName: string);
|
|
var
|
|
FindData: TPRFindData;
|
|
Ref: TResolvedReference;
|
|
DeclEl: TPasElement;
|
|
begin
|
|
// e.g. Name[]
|
|
DeclEl:=FindElementWithoutParams(ArrayName,FindData,Value,true);
|
|
Ref:=CreateReference(DeclEl,Value,Access,@FindData);
|
|
CheckFoundElement(FindData,Ref);
|
|
ComputeElement(Value,ResolvedEl,[rcSkipTypeAlias]);
|
|
end;
|
|
|
|
var
|
|
Value: TPasExpr;
|
|
SubParams: TParamsExpr;
|
|
begin
|
|
Value:=Params.Value;
|
|
if (Value.ClassType=TPrimitiveExpr)
|
|
and (TPrimitiveExpr(Value).Kind=pekIdent) then
|
|
// e.g. Name[]
|
|
ResolveValueName(Value,TPrimitiveExpr(Value).Value)
|
|
else if (Value.ClassType=TSelfExpr) then
|
|
// e.g. Self[]
|
|
ResolveValueName(Value,'Self')
|
|
else if Value.ClassType=TParamsExpr then
|
|
begin
|
|
SubParams:=TParamsExpr(Value);
|
|
if (SubParams.Kind in [pekArrayParams,pekFuncParams]) then
|
|
begin
|
|
// e.g. Name()[] or Name[][]
|
|
ResolveExpr(SubParams,rraRead);
|
|
ComputeElement(SubParams,ResolvedEl,[rcSkipTypeAlias,rcNoImplicitProc]);
|
|
CreateReference(ResolvedEl.TypeEl,Value,Access);
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20161010194925,Value);
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20160927212610,Value);
|
|
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveArrayParamsExpr Value=',GetObjName(Value),' ',GetResolverResultDesc(ResolvedEl));
|
|
{$ENDIF}
|
|
ResolveArrayParamsArgs(Params,ResolvedEl,Access);
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveArrayParamsArgs(Params: TParamsExpr;
|
|
const ResolvedValue: TPasResolverResult; Access: TResolvedRefAccess);
|
|
var
|
|
ArgExp: TPasExpr;
|
|
ResolvedArg: TPasResolverResult;
|
|
PropEl: TPasProperty;
|
|
ClassScope: TPasClassScope;
|
|
i: Integer;
|
|
begin
|
|
if ResolvedValue.BaseType in btAllStrings then
|
|
begin
|
|
// string -> check that ResolvedValue is not merely a type, but has a value
|
|
if not ResolvedElHasValue(ResolvedValue) then
|
|
RaiseXExpectedButYFound(20170216152548,'variable',ResolvedValue.TypeEl.ElementTypeName,Params);
|
|
// check single argument
|
|
if length(Params.Params)<1 then
|
|
RaiseMsg(20170216152204,nMissingParameterX,
|
|
sMissingParameterX,['character index'],Params)
|
|
else if length(Params.Params)>1 then
|
|
RaiseMsg(20170216152551,nIllegalQualifier,sIllegalQualifier,[','],Params.Params[1]);
|
|
// check argument is integer
|
|
ArgExp:=Params.Params[0];
|
|
ComputeElement(ArgExp,ResolvedArg,[rcSkipTypeAlias]);
|
|
if not (ResolvedArg.BaseType in btAllInteger) then
|
|
RaiseMsg(20170216152209,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected,
|
|
[BaseTypeNames[ResolvedArg.BaseType],BaseTypeNames[BaseTypeStringIndex]],ArgExp);
|
|
if not (rrfReadable in ResolvedArg.Flags) then
|
|
RaiseMsg(20170216152211,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected,
|
|
['type','value'],ArgExp);
|
|
AccessExpr(ArgExp,rraRead);
|
|
exit;
|
|
end
|
|
else if (ResolvedValue.IdentEl is TPasProperty)
|
|
and (TPasProperty(ResolvedValue.IdentEl).Args.Count>0) then
|
|
begin
|
|
PropEl:=TPasProperty(ResolvedValue.IdentEl);
|
|
CheckCallPropertyCompatibility(PropEl,Params,true);
|
|
FinishPropertyParamAccess(Params,PropEl);
|
|
exit;
|
|
end
|
|
else if ResolvedValue.BaseType=btContext then
|
|
begin
|
|
if ResolvedValue.TypeEl.ClassType=TPasClassType then
|
|
begin
|
|
ClassScope:=ResolvedValue.TypeEl.CustomData as TPasClassScope;
|
|
if ResolveBracketOperatorClass(Params,ResolvedValue,ClassScope,Access) then
|
|
exit;
|
|
end
|
|
else if ResolvedValue.TypeEl.ClassType=TPasArrayType then
|
|
begin
|
|
if ResolvedValue.IdentEl is TPasType then
|
|
RaiseMsg(20170216152215,nIllegalQualifier,sIllegalQualifier,['['],Params);
|
|
CheckCallArrayCompatibility(TPasArrayType(ResolvedValue.TypeEl),Params,true);
|
|
for i:=0 to length(Params.Params)-1 do
|
|
AccessExpr(Params.Params[i],rraRead);
|
|
exit;
|
|
end;
|
|
end;
|
|
RaiseMsg(20170216152217,nIllegalQualifier,sIllegalQualifier,['['],Params);
|
|
end;
|
|
|
|
function TPasResolver.ResolveBracketOperatorClass(Params: TParamsExpr;
|
|
const ResolvedValue: TPasResolverResult; ClassScope: TPasClassScope;
|
|
Access: TResolvedRefAccess): boolean;
|
|
var
|
|
PropEl: TPasProperty;
|
|
Value: TPasExpr;
|
|
begin
|
|
PropEl:=ClassScope.DefaultProperty;
|
|
if PropEl<>nil then
|
|
begin
|
|
// class has default property
|
|
if (ResolvedValue.IdentEl is TPasType) and (not PropEl.IsClass) then
|
|
RaiseMsg(20170216152213,nIllegalQualifier,sIllegalQualifier,['['],Params);
|
|
Value:=Params.Value;
|
|
if Value.CustomData is TResolvedReference then
|
|
SetResolvedRefAccess(Value,TResolvedReference(Value.CustomData),rraRead);
|
|
CreateReference(PropEl,Params,Access);
|
|
CheckCallPropertyCompatibility(PropEl,Params,true);
|
|
FinishPropertyParamAccess(Params,PropEl);
|
|
exit(true);
|
|
end;
|
|
Result:=false;
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveSetParamsExpr(Params: TParamsExpr);
|
|
// e.g. resolving '[1,2..3]'
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResolveSetParamsExpr ',GetTreeDesc(Params));
|
|
{$ENDIF}
|
|
if Params.Value<>nil then
|
|
RaiseNotYetImplemented(20160930135910,Params);
|
|
end;
|
|
|
|
procedure TPasResolver.ResolveArrayValues(El: TArrayValues);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to length(El.Values)-1 do
|
|
ResolveExpr(El.Values[i],rraRead);
|
|
end;
|
|
|
|
procedure TPasResolver.SetResolvedRefAccess(Expr: TPasExpr;
|
|
Ref: TResolvedReference; Access: TResolvedRefAccess);
|
|
begin
|
|
if (Ref.Access=Access) then exit;
|
|
if Access in [rraNone,rraParamToUnknownProc] then
|
|
exit;
|
|
if Expr=nil then ;
|
|
|
|
case Ref.Access of
|
|
rraNone,rraParamToUnknownProc:
|
|
Ref.Access:=Access;
|
|
rraRead:
|
|
if Access in [rraAssign,rraReadAndAssign,rraVarParam,rraOutParam] then
|
|
Ref.Access:=rraReadAndAssign
|
|
else
|
|
exit;
|
|
rraAssign,rraOutParam:
|
|
if Access in [rraRead,rraReadAndAssign,rraVarParam] then
|
|
Ref.Access:=rraReadAndAssign
|
|
else
|
|
exit;
|
|
rraReadAndAssign: exit;
|
|
rraVarParam: exit;
|
|
else
|
|
RaiseInternalError(20170403163727);
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.AccessExpr(Expr: TPasExpr;
|
|
Access: TResolvedRefAccess);
|
|
// called after a call target was found, called for each element
|
|
// to set the rraParamToUnknownProc to Access
|
|
var
|
|
Ref: TResolvedReference;
|
|
Bin: TBinaryExpr;
|
|
Params: TParamsExpr;
|
|
ValueResolved: TPasResolverResult;
|
|
C: TClass;
|
|
begin
|
|
if (Expr.CustomData is TResolvedReference) then
|
|
begin
|
|
Ref:=TResolvedReference(Expr.CustomData);
|
|
SetResolvedRefAccess(Expr,Ref,Access);
|
|
end;
|
|
|
|
C:=Expr.ClassType;
|
|
if C=TBinaryExpr then
|
|
begin
|
|
Bin:=TBinaryExpr(Expr);
|
|
if Bin.OpCode in [eopSubIdent,eopNone] then
|
|
AccessExpr(Bin.right,Access);
|
|
end
|
|
else if C=TParamsExpr then
|
|
begin
|
|
Params:=TParamsExpr(Expr);
|
|
case Params.Kind of
|
|
pekFuncParams:
|
|
if IsTypeCast(Params) then
|
|
AccessExpr(Params.Params[0],Access)
|
|
else
|
|
AccessExpr(Params.Value,Access);
|
|
pekArrayParams:
|
|
begin
|
|
ComputeElement(Params.Value,ValueResolved,[]);
|
|
if not IsDynArray(ValueResolved.TypeEl) then
|
|
AccessExpr(Params.Value,Access);
|
|
end;
|
|
pekSet:
|
|
if Access<>rraRead then
|
|
RaiseMsg(20170306112306,nVariableIdentifierExpected,sVariableIdentifierExpected,[],Expr);
|
|
else
|
|
RaiseNotYetImplemented(20170403173831,Params);
|
|
end;
|
|
end
|
|
else if (C=TSelfExpr) or ((C=TPrimitiveExpr) and (TPrimitiveExpr(Expr).Kind=pekIdent)) then
|
|
// ok
|
|
else if (Access=rraRead)
|
|
and ((C=TPrimitiveExpr)
|
|
or (C=TNilExpr)
|
|
or (C=TBoolConstExpr)
|
|
or (C=TUnaryExpr)) then
|
|
// ok
|
|
else
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.AccessExpr Expr=',GetObjName(Expr),' Access=',Access,' Declaration="',Expr.GetDeclaration(false),'"');
|
|
{$ENDIF}
|
|
RaiseNotYetImplemented(20170306102158,Expr);
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.CheckPendingForwards(El: TPasElement);
|
|
var
|
|
i: Integer;
|
|
DeclEl: TPasElement;
|
|
Proc: TPasProcedure;
|
|
aClassType: TPasClassType;
|
|
begin
|
|
if El is TPasDeclarations then
|
|
begin
|
|
for i:=0 to TPasDeclarations(El).Declarations.Count-1 do
|
|
begin
|
|
DeclEl:=TPasElement(TPasDeclarations(El).Declarations[i]);
|
|
if DeclEl is TPasProcedure then
|
|
begin
|
|
Proc:=TPasProcedure(DeclEl);
|
|
if ProcNeedsImplProc(Proc)
|
|
and (TPasProcedureScope(Proc.CustomData).ImplProc=nil) then
|
|
RaiseMsg(20170216152219,nForwardProcNotResolved,sForwardProcNotResolved,
|
|
[Proc.ElementTypeName,Proc.Name],Proc);
|
|
end;
|
|
end;
|
|
end
|
|
else if El.ClassType=TPasClassType then
|
|
begin
|
|
aClassType:=TPasClassType(El);
|
|
for i:=0 to aClassType.Members.Count-1 do
|
|
begin
|
|
DeclEl:=TPasElement(aClassType.Members[i]);
|
|
if DeclEl is TPasProcedure then
|
|
begin
|
|
Proc:=TPasProcedure(DeclEl);
|
|
if Proc.IsAbstract or Proc.IsExternal then continue;
|
|
if TPasProcedureScope(Proc.CustomData).ImplProc=nil then
|
|
RaiseMsg(20170216152221,nForwardProcNotResolved,sForwardProcNotResolved,
|
|
[Proc.ElementTypeName,Proc.Name],Proc);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.AddModule(El: TPasModule);
|
|
begin
|
|
if TopScope<>DefaultScope then
|
|
RaiseInvalidScopeForElement(20160922163504,El);
|
|
PushScope(El,TPasModuleScope);
|
|
TPasModuleScope(TopScope).VisibilityContext:=El;
|
|
end;
|
|
|
|
procedure TPasResolver.AddSection(El: TPasSection);
|
|
// TInterfaceSection, TImplementationSection, TProgramSection, TLibrarySection
|
|
// Note: implementation scope is within the interface scope
|
|
begin
|
|
FPendingForwards.Add(El); // check forward declarations at the end
|
|
PushScope(El,TPasSectionScope);
|
|
end;
|
|
|
|
procedure TPasResolver.AddType(El: TPasType);
|
|
begin
|
|
if (El.Name='') then exit; // sub type
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.AddType El=',GetObjName(El),' El.Parent=',GetObjName(El.Parent));
|
|
{$ENDIF}
|
|
if not (TopScope is TPasIdentifierScope) then
|
|
RaiseInvalidScopeForElement(20160922163506,El);
|
|
AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
|
|
end;
|
|
|
|
procedure TPasResolver.AddRecordType(El: TPasRecordType);
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.AddRecordType ',GetObjName(El),' Parent=',GetObjName(El.Parent));
|
|
{$ENDIF}
|
|
if not (TopScope is TPasIdentifierScope) then
|
|
RaiseInvalidScopeForElement(20160922163508,El);
|
|
if El.Name<>'' then begin
|
|
AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
|
|
FPendingForwards.Add(El); // check forward declarations at the end
|
|
end;
|
|
|
|
if El.Parent.ClassType<>TPasVariant then
|
|
PushScope(El,TPasRecordScope);
|
|
end;
|
|
|
|
procedure TPasResolver.AddClassType(El: TPasClassType);
|
|
var
|
|
Duplicate: TPasIdentifier;
|
|
ForwardDecl: TPasClassType;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.AddClassType ',GetObjName(El),' Parent=',GetObjName(El.Parent),' ',GetElementSourcePosStr(El));
|
|
{$ENDIF}
|
|
if not (TopScope is TPasIdentifierScope) then
|
|
RaiseInvalidScopeForElement(20160922163510,El);
|
|
|
|
Duplicate:=TPasIdentifierScope(TopScope).FindIdentifier(El.Name);
|
|
//if Duplicate<>nil then
|
|
//writeln(' Duplicate=',GetObjName(Duplicate.Element),' ',ord(Duplicate.Kind));
|
|
|
|
if (Duplicate<>nil)
|
|
and (Duplicate.Kind=pikSimple)
|
|
and (Duplicate.Element<>nil)
|
|
and (Duplicate.Element.Parent=El.Parent)
|
|
and (Duplicate.Element is TPasClassType)
|
|
and TPasClassType(Duplicate.Element).IsForward
|
|
then
|
|
begin
|
|
// forward declaration found
|
|
ForwardDecl:=TPasClassType(Duplicate.Element);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln(' Resolving Forward=',GetObjName(ForwardDecl),' ',GetElementSourcePosStr(ForwardDecl));
|
|
{$ENDIF}
|
|
if ForwardDecl.CustomData<>nil then
|
|
RaiseInternalError(20160922163513,'forward class has already customdata');
|
|
// create a ref from the forward to the real declaration
|
|
CreateReference(El,ForwardDecl,rraRead);
|
|
// change the cache item
|
|
Duplicate.Element:=El;
|
|
end
|
|
else
|
|
AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
|
|
|
|
FPendingForwards.Add(El); // check forward declarations at the end
|
|
end;
|
|
|
|
procedure TPasResolver.AddVariable(El: TPasVariable);
|
|
begin
|
|
if (El.Name='') then exit; // anonymous var
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.AddVariable ',GetObjName(El));
|
|
{$ENDIF}
|
|
if not (TopScope is TPasIdentifierScope) then
|
|
RaiseInvalidScopeForElement(20160929205730,El);
|
|
AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
|
|
end;
|
|
|
|
procedure TPasResolver.AddEnumType(El: TPasEnumType);
|
|
var
|
|
CanonicalSet: TPasSetType;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.AddEnumType ',GetObjName(El));
|
|
{$ENDIF}
|
|
if not (TopScope is TPasIdentifierScope) then
|
|
RaiseInvalidScopeForElement(20160929205732,El);
|
|
AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
|
|
PushScope(El,TPasEnumTypeScope);
|
|
// add canonical set
|
|
CanonicalSet:=TPasSetType.Create('',El);
|
|
CanonicalSet.EnumType:=El;
|
|
El.AddRef;
|
|
TPasEnumTypeScope(TopScope).CanonicalSet:=CanonicalSet;
|
|
end;
|
|
|
|
procedure TPasResolver.AddEnumValue(El: TPasEnumValue);
|
|
var
|
|
i: Integer;
|
|
Scope: TPasScope;
|
|
Old: TPasIdentifier;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.AddEnumValue ',GetObjName(El));
|
|
{$ENDIF}
|
|
if not (TopScope is TPasEnumTypeScope) then
|
|
RaiseInvalidScopeForElement(20160929205736,El);
|
|
AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
|
|
|
|
// propagate enum to parent scopes
|
|
for i:=ScopeCount-2 downto 0 do
|
|
begin
|
|
Scope:=Scopes[i];
|
|
if (Scope is TPasClassScope) or (Scope is TPasRecordScope) then
|
|
begin
|
|
// class or record: add if not duplicate
|
|
Old:=TPasIdentifierScope(Scope).FindIdentifier(El.Name);
|
|
if Old=nil then
|
|
TPasIdentifierScope(Scope).AddIdentifier(El.Name,El,pikSimple);
|
|
end
|
|
else if (Scope is TPasProcedureScope) or (Scope is TPasSectionScope) then
|
|
begin
|
|
// procedure or section: check for duplicate and add
|
|
Old:=TPasIdentifierScope(Scope).FindLocalIdentifier(El.Name);
|
|
if Old<>nil then
|
|
RaiseMsg(20170216152224,nDuplicateIdentifier,sDuplicateIdentifier,
|
|
[El.Name,GetElementSourcePosStr(Old.Element)],El);
|
|
TPasIdentifierScope(Scope).AddIdentifier(El.Name,El,pikSimple);
|
|
break;
|
|
end
|
|
else
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.AddProperty(El: TPasProperty);
|
|
begin
|
|
if (El.Name='') then
|
|
RaiseNotYetImplemented(20160922163518,El);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.AddProperty ',GetObjName(El));
|
|
{$ENDIF}
|
|
if not (TopScope is TPasClassScope) then
|
|
RaiseInvalidScopeForElement(20160922163520,El);
|
|
AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
|
|
PushScope(El,TPasPropertyScope);
|
|
end;
|
|
|
|
procedure TPasResolver.AddProcedure(El: TPasProcedure);
|
|
var
|
|
ProcName, aClassName: String;
|
|
p: SizeInt;
|
|
CurClassType: TPasClassType;
|
|
ProcScope: TPasProcedureScope;
|
|
NeedPop, HasDot: Boolean;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.AddProcedure ',GetObjName(El));
|
|
{$ENDIF}
|
|
if not (TopScope is TPasIdentifierScope) then
|
|
RaiseInvalidScopeForElement(20160922163522,El);
|
|
// Note: El.ProcType is nil !
|
|
ProcName:=El.Name;
|
|
HasDot:=Pos('.',ProcName)>1;
|
|
if not HasDot then
|
|
AddIdentifier(TPasIdentifierScope(TopScope),ProcName,El,pikProc);
|
|
ProcScope:=TPasProcedureScope(PushScope(El,TPasProcedureScope));
|
|
if HasDot then
|
|
begin
|
|
// method implementation -> search class
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.AddProcedure searching class of "',ProcName,'" ...');
|
|
{$ENDIF}
|
|
CurClassType:=nil;
|
|
repeat
|
|
p:=Pos('.',ProcName);
|
|
if p<1 then
|
|
begin
|
|
if CurClassType=nil then
|
|
RaiseInternalError(20161013170829);
|
|
break;
|
|
end;
|
|
aClassName:=LeftStr(ProcName,p-1);
|
|
Delete(ProcName,1,p);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.AddProcedure searching class "',aClassName,'" ProcName="',ProcName,'" ...');
|
|
{$ENDIF}
|
|
if not IsValidIdent(aClassName) then
|
|
RaiseNotYetImplemented(20161013170844,El);
|
|
|
|
if CurClassType<>nil then
|
|
begin
|
|
NeedPop:=true;
|
|
PushClassDotScope(CurClassType);
|
|
end
|
|
else
|
|
NeedPop:=false;
|
|
|
|
CurClassType:=TPasClassType(FindElementWithoutParams(aClassName,El,false));
|
|
if not (CurClassType is TPasClassType) then
|
|
begin
|
|
aClassName:=LeftStr(El.Name,length(El.Name)-length(ProcName));
|
|
RaiseXExpectedButYFound(20170216152557,'class',aClassname+':'+CurClassType.ElementTypeName,El);
|
|
end;
|
|
|
|
// restore scope
|
|
if NeedPop then
|
|
PopScope;
|
|
until false;
|
|
|
|
if not IsValidIdent(ProcName) then
|
|
RaiseNotYetImplemented(20161013170956,El);
|
|
|
|
ProcScope.VisibilityContext:=CurClassType;
|
|
ProcScope.ClassScope:=CurClassType.CustomData as TPasClassScope;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.AddArgument(El: TPasArgument);
|
|
var
|
|
ProcType: TPasProcedureType;
|
|
i: Integer;
|
|
Arg: TPasArgument;
|
|
begin
|
|
if (El.Name='') then
|
|
RaiseInternalError(20160922163526,GetObjName(El));
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.AddArgument ',GetObjName(El));
|
|
{$ENDIF}
|
|
if (TopScope=nil) then
|
|
RaiseInvalidScopeForElement(20160922163529,El);
|
|
if El.Parent.ClassType=TPasProperty then
|
|
begin
|
|
if TopScope.ClassType<>TPasPropertyScope then
|
|
RaiseInvalidScopeForElement(20161014124530,El);
|
|
AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
|
|
end
|
|
else if El.Parent is TPasProcedureType then
|
|
begin
|
|
ProcType:=TPasProcedureType(El.Parent);
|
|
if ProcType.Parent is TPasProcedure then
|
|
begin
|
|
if TopScope.ClassType<>TPasProcedureScope then
|
|
RaiseInvalidScopeForElement(20160922163529,El);
|
|
AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
|
|
end
|
|
else
|
|
begin
|
|
for i:=0 to ProcType.Args.Count-1 do
|
|
begin
|
|
Arg:=TPasArgument(ProcType.Args[i]);
|
|
if (Arg<>El) and (CompareText(TPasArgument(ProcType.Args[i]).Name,El.Name)=0) then
|
|
RaiseMsg(20170216152225,nDuplicateIdentifier,sDuplicateIdentifier,[Arg.Name,GetElementSourcePosStr(Arg)],El);
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20161014124937,El);
|
|
end;
|
|
|
|
procedure TPasResolver.AddFunctionResult(El: TPasResultElement);
|
|
begin
|
|
if TopScope.ClassType<>TPasProcedureScope then exit;
|
|
AddIdentifier(TPasProcedureScope(TopScope),ResolverResultVar,El,pikSimple);
|
|
end;
|
|
|
|
procedure TPasResolver.AddExceptOn(El: TPasImplExceptOn);
|
|
begin
|
|
PushScope(El,TPasExceptOnScope);
|
|
end;
|
|
|
|
procedure TPasResolver.AddProcedureBody(El: TProcedureBody);
|
|
begin
|
|
if El=nil then ;
|
|
CheckTopScope(TPasProcedureScope);
|
|
end;
|
|
|
|
procedure TPasResolver.WriteScopes;
|
|
var
|
|
i: Integer;
|
|
Scope: TPasScope;
|
|
begin
|
|
writeln('TPasResolver.WriteScopes ScopeCount=',ScopeCount);
|
|
for i:=ScopeCount-1 downto 0 do
|
|
begin
|
|
Scope:=Scopes[i];
|
|
writeln(' ',i,'/',ScopeCount,' ',GetObjName(Scope));
|
|
Scope.WriteIdentifiers(' ');
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.ComputeBinaryExpr(Bin: TBinaryExpr; out
|
|
ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
|
|
StartEl: TPasElement);
|
|
|
|
procedure SetBaseType(BaseType: TResolverBaseType);
|
|
begin
|
|
SetResolverValueExpr(ResolvedEl,BaseType,FBaseTypes[BaseType],Bin,[rrfReadable]);
|
|
end;
|
|
|
|
var
|
|
LeftResolved, RightResolved: TPasResolverResult;
|
|
LeftTypeEl, RightTypeEl: TPasType;
|
|
begin
|
|
if (Bin.OpCode=eopSubIdent)
|
|
or ((Bin.OpCode=eopNone) and (Bin.left is TInheritedExpr)) then
|
|
begin
|
|
// Note: bin.left was already resolved via ResolveSubIdent
|
|
ComputeElement(Bin.right,ResolvedEl,Flags,StartEl);
|
|
exit;
|
|
end;
|
|
|
|
if Bin.OpCode in [eopEqual,eopNotEqual] then
|
|
begin
|
|
if CheckEqualElCompatibility(Bin.left,Bin.right,nil,true)=cIncompatible then
|
|
RaiseInternalError(20161007215912);
|
|
SetBaseType(btBoolean);
|
|
exit;
|
|
end;
|
|
|
|
ComputeElement(Bin.left,LeftResolved,Flags-[rcNoImplicitProc],StartEl);
|
|
ComputeElement(Bin.right,RightResolved,Flags-[rcNoImplicitProc],StartEl);
|
|
// ToDo: check operator overloading
|
|
|
|
//writeln('TPasResolver.ComputeBinaryExpr ',OpcodeStrings[Bin.OpCode],' Left=',GetResolverResultDesc(LeftResolved),' Right=',GetResolverResultDesc(RightResolved));
|
|
|
|
if LeftResolved.BaseType in btAllInteger then
|
|
begin
|
|
if (rrfReadable in LeftResolved.Flags)
|
|
and (rrfReadable in RightResolved.Flags) then
|
|
begin
|
|
if (RightResolved.BaseType in (btAllInteger+btAllFloats)) then
|
|
case Bin.OpCode of
|
|
eopNone:
|
|
if (Bin.Kind=pekRange) then
|
|
begin
|
|
if not (RightResolved.BaseType in btAllInteger) then
|
|
RaiseXExpectedButYFound(20170216152600,'integer',BaseTypeNames[RightResolved.BaseType],Bin.right);
|
|
SetResolverValueExpr(ResolvedEl,LeftResolved.BaseType,LeftResolved.TypeEl,Bin,[rrfReadable]);
|
|
exit;
|
|
end;
|
|
eopAdd, eopSubtract,
|
|
eopMultiply, eopDiv, eopMod,
|
|
eopPower,
|
|
eopShl, eopShr,
|
|
eopAnd, eopOr, eopXor:
|
|
begin
|
|
// use left type for result
|
|
SetResolverValueExpr(ResolvedEl,LeftResolved.BaseType,LeftResolved.TypeEl,Bin,[rrfReadable]);
|
|
exit;
|
|
end;
|
|
eopLessThan,
|
|
eopGreaterThan,
|
|
eopLessthanEqual,
|
|
eopGreaterThanEqual:
|
|
begin
|
|
SetBaseType(btBoolean);
|
|
exit;
|
|
end;
|
|
end
|
|
else if (RightResolved.BaseType=btSet) and (RightResolved.SubType in btAllInteger)
|
|
and (Bin.OpCode=eopIn) then
|
|
begin
|
|
SetBaseType(btBoolean);
|
|
exit;
|
|
end;
|
|
end;
|
|
end
|
|
else if LeftResolved.BaseType in btAllBooleans then
|
|
begin
|
|
if (rrfReadable in LeftResolved.Flags)
|
|
and (RightResolved.BaseType in btAllBooleans)
|
|
and (rrfReadable in RightResolved.Flags) then
|
|
case Bin.OpCode of
|
|
eopNone:
|
|
if Bin.Kind=pekRange then
|
|
begin
|
|
SetResolverValueExpr(ResolvedEl,btRange,FBaseTypes[LeftResolved.BaseType],Bin,[rrfReadable]);
|
|
ResolvedEl.SubType:=LeftResolved.BaseType;
|
|
exit;
|
|
end;
|
|
eopAnd, eopOr, eopXor:
|
|
begin
|
|
// use left type for result
|
|
SetResolverValueExpr(ResolvedEl,LeftResolved.BaseType,LeftResolved.TypeEl,Bin,[rrfReadable]);
|
|
exit;
|
|
end;
|
|
end;
|
|
end
|
|
else if LeftResolved.BaseType in btAllStringAndChars then
|
|
begin
|
|
if (rrfReadable in LeftResolved.Flags)
|
|
and (rrfReadable in RightResolved.Flags) then
|
|
begin
|
|
if (RightResolved.BaseType in btAllStringAndChars) then
|
|
case Bin.OpCode of
|
|
eopNone:
|
|
if (Bin.Kind=pekRange) and (LeftResolved.BaseType in [btChar]) then
|
|
begin
|
|
if RightResolved.BaseType<>btChar then
|
|
RaiseXExpectedButYFound(20170216152603,'char',BaseTypeNames[RightResolved.BaseType],Bin.right);
|
|
SetResolverValueExpr(ResolvedEl,btRange,FBaseTypes[btChar],Bin,[rrfReadable]);
|
|
ResolvedEl.SubType:=LeftResolved.BaseType;
|
|
exit;
|
|
end;
|
|
eopAdd:
|
|
case LeftResolved.BaseType of
|
|
btChar:
|
|
begin
|
|
case RightResolved.BaseType of
|
|
btChar: SetBaseType(btString);
|
|
btWideChar: SetBaseType(btUnicodeString);
|
|
else
|
|
// use right type for result
|
|
SetResolverValueExpr(ResolvedEl,RightResolved.BaseType,RightResolved.TypeEl,Bin,[rrfReadable]);
|
|
end;
|
|
exit;
|
|
end;
|
|
btWideChar:
|
|
begin
|
|
SetBaseType(btUnicodeString);
|
|
exit;
|
|
end;
|
|
btShortString:
|
|
begin
|
|
case RightResolved.BaseType of
|
|
btChar,btShortString,btWideChar:
|
|
// use left type for result
|
|
SetResolverValueExpr(ResolvedEl,LeftResolved.BaseType,LeftResolved.TypeEl,Bin,[rrfReadable]);
|
|
else
|
|
// shortstring + string => string
|
|
SetResolverValueExpr(ResolvedEl,RightResolved.BaseType,RightResolved.TypeEl,Bin,[rrfReadable]);
|
|
end;
|
|
exit;
|
|
end;
|
|
btString,btAnsiString,btUnicodeString:
|
|
begin
|
|
// string + x => string
|
|
SetResolverValueExpr(ResolvedEl,LeftResolved.BaseType,LeftResolved.TypeEl,Bin,[rrfReadable]);
|
|
exit;
|
|
end;
|
|
end;
|
|
eopLessThan,
|
|
eopGreaterThan,
|
|
eopLessthanEqual,
|
|
eopGreaterThanEqual:
|
|
begin
|
|
SetBaseType(btBoolean);
|
|
exit;
|
|
end;
|
|
end
|
|
else if (RightResolved.BaseType=btSet) and (RightResolved.SubType=btChar)
|
|
and (LeftResolved.BaseType=btChar) then
|
|
begin
|
|
case Bin.OpCode of
|
|
eopIn:
|
|
begin
|
|
SetBaseType(btBoolean);
|
|
exit;
|
|
end;
|
|
end;
|
|
end
|
|
end
|
|
end
|
|
else if LeftResolved.BaseType in btAllFloats then
|
|
begin
|
|
if (rrfReadable in LeftResolved.Flags)
|
|
and (RightResolved.BaseType in (btAllInteger+btAllFloats))
|
|
and (rrfReadable in RightResolved.Flags) then
|
|
case Bin.OpCode of
|
|
eopAdd, eopSubtract,
|
|
eopMultiply, eopDivide, eopMod,
|
|
eopPower:
|
|
begin
|
|
SetResolverValueExpr(ResolvedEl,LeftResolved.BaseType,LeftResolved.TypeEl,Bin,[rrfReadable]);
|
|
exit;
|
|
end;
|
|
eopLessThan,
|
|
eopGreaterThan,
|
|
eopLessthanEqual,
|
|
eopGreaterThanEqual:
|
|
begin
|
|
SetBaseType(btBoolean);
|
|
exit;
|
|
end;
|
|
end;
|
|
end
|
|
else if LeftResolved.BaseType=btPointer then
|
|
begin
|
|
if (rrfReadable in LeftResolved.Flags)
|
|
and (RightResolved.BaseType in btAllInteger)
|
|
and (rrfReadable in RightResolved.Flags) then
|
|
case Bin.OpCode of
|
|
eopAdd,eopSubtract:
|
|
begin
|
|
SetResolverValueExpr(ResolvedEl,btPointer,LeftResolved.TypeEl,Bin,[rrfReadable]);
|
|
exit;
|
|
end;
|
|
end
|
|
else if RightResolved.BaseType=btPointer then
|
|
case Bin.OpCode of
|
|
eopLessThan,
|
|
eopGreaterThan,
|
|
eopLessthanEqual,
|
|
eopGreaterThanEqual:
|
|
begin
|
|
SetBaseType(btBoolean);
|
|
exit;
|
|
end;
|
|
end;
|
|
end
|
|
else if LeftResolved.BaseType=btContext then
|
|
case Bin.OpCode of
|
|
eopNone:
|
|
if Bin.Kind=pekRange then
|
|
begin
|
|
if (rrfReadable in LeftResolved.Flags)
|
|
and (rrfReadable in RightResolved.Flags) then
|
|
begin
|
|
CheckSetElementsCompatible(Bin.left,Bin.right,LeftResolved,RightResolved);
|
|
ResolvedEl:=LeftResolved;
|
|
ResolvedEl.SubType:=ResolvedEl.BaseType;
|
|
ResolvedEl.BaseType:=btRange;
|
|
exit;
|
|
end;
|
|
end;
|
|
eopIn:
|
|
if (rrfReadable in LeftResolved.Flags)
|
|
and (rrfReadable in RightResolved.Flags) then
|
|
begin
|
|
if LeftResolved.BaseType in (btAllInteger+[btChar]) then
|
|
begin
|
|
if (RightResolved.BaseType<>btSet) then
|
|
RaiseXExpectedButYFound(20170216152607,'set of '+BaseTypeNames[LeftResolved.BaseType],LeftResolved.TypeEl.ElementTypeName,Bin.right);
|
|
if LeftResolved.BaseType=btChar then
|
|
begin
|
|
if RightResolved.SubType<>btChar then
|
|
RaiseXExpectedButYFound(20170216152609,'set of '+BaseTypeNames[LeftResolved.BaseType],'set of '+BaseTypeNames[RightResolved.SubType],Bin.right);
|
|
end
|
|
else if not (RightResolved.SubType in btAllInteger) then
|
|
RaiseXExpectedButYFound(20170216152612,'set of '+BaseTypeNames[LeftResolved.BaseType],'set of '+BaseTypeNames[RightResolved.SubType],Bin.right);
|
|
SetBaseType(btBoolean);
|
|
exit;
|
|
end
|
|
else if (LeftResolved.BaseType=btContext) and (LeftResolved.TypeEl is TPasEnumType) then
|
|
begin
|
|
if (RightResolved.BaseType<>btSet) then
|
|
RaiseXExpectedButYFound(20170216152615,'set of '+LeftResolved.TypeEl.Name,LeftResolved.TypeEl.ElementTypeName,Bin.right);
|
|
if LeftResolved.TypeEl<>RightResolved.TypeEl then
|
|
RaiseXExpectedButYFound(20170216152618,'set of '+LeftResolved.TypeEl.Name,'set of '+RightResolved.TypeEl.Name,Bin.right);
|
|
SetBaseType(btBoolean);
|
|
exit;
|
|
end
|
|
else
|
|
RaiseMsg(20170216152228,nInOperatorExpectsSetElementButGot,
|
|
sInOperatorExpectsSetElementButGot,[LeftResolved.TypeEl.ElementTypeName],Bin);
|
|
end;
|
|
eopIs:
|
|
begin
|
|
if (LeftResolved.TypeEl is TPasClassType) then
|
|
begin
|
|
if (LeftResolved.IdentEl=nil) or (LeftResolved.IdentEl is TPasType) then
|
|
RaiseMsg(20170216152230,nIllegalQualifier,sIllegalQualifier,['is'],Bin);
|
|
// left side is a class instance
|
|
if RightResolved.IdentEl is TPasClassType then
|
|
begin
|
|
// e.g. if Image is TFPMemoryImage then ;
|
|
// Note: at compile time the check is reversed: right must inherit from left
|
|
if CheckSrcIsADstType(RightResolved,LeftResolved,Bin)<>cIncompatible then
|
|
begin
|
|
SetBaseType(btBoolean);
|
|
exit;
|
|
end
|
|
end
|
|
else if (RightResolved.TypeEl is TPasClassOfType)
|
|
and (rrfReadable in RightResolved.Flags) then
|
|
begin
|
|
// e.g. if Image is ImageClass then ;
|
|
if (CheckClassesAreRelated(LeftResolved.TypeEl,
|
|
TPasClassOfType(RightResolved.TypeEl).DestType,Bin)<>cIncompatible) then
|
|
begin
|
|
SetBaseType(btBoolean);
|
|
exit;
|
|
end
|
|
end
|
|
else
|
|
RaiseXExpectedButYFound(20170216152625,'class type',RightResolved.TypeEl.ElementTypeName,Bin.right);
|
|
end
|
|
else if (proClassOfIs in Options) and (LeftResolved.TypeEl is TPasClassOfType)
|
|
and (rrfReadable in LeftResolved.Flags) then
|
|
begin
|
|
if (LeftResolved.IdentEl=nil) or (LeftResolved.IdentEl is TPasType) then
|
|
RaiseMsg(20170322101128,nIllegalQualifier,sIllegalQualifier,['is'],Bin);
|
|
// left side is class-of variable
|
|
LeftTypeEl:=TPasClassOfType(LeftResolved.TypeEl).DestType;
|
|
if RightResolved.IdentEl is TPasClassType then
|
|
begin
|
|
// e.g. if ImageClass is TFPMemoryImage then ;
|
|
// Note: at compile time the check is reversed: right must inherit from left
|
|
if CheckClassIsClass(RightResolved.TypeEl,LeftTypeEl,Bin)<>cIncompatible then
|
|
begin
|
|
SetBaseType(btBoolean);
|
|
exit;
|
|
end
|
|
end
|
|
else if (RightResolved.TypeEl is TPasClassOfType) then
|
|
begin
|
|
// e.g. if ImageClassA is ImageClassB then ;
|
|
// or if ImageClassA is TFPImageClass then ;
|
|
RightTypeEl:=TPasClassOfType(RightResolved.TypeEl).DestType;
|
|
if (CheckClassesAreRelated(LeftTypeEl,RightTypeEl,Bin)<>cIncompatible) then
|
|
begin
|
|
SetBaseType(btBoolean);
|
|
exit;
|
|
end
|
|
end
|
|
else
|
|
RaiseXExpectedButYFound(20170322105252,'class type',RightResolved.TypeEl.ElementTypeName,Bin.right);
|
|
end
|
|
else if LeftResolved.TypeEl=nil then
|
|
RaiseMsg(20170216152232,nLeftSideOfIsOperatorExpectsAClassButGot,sLeftSideOfIsOperatorExpectsAClassButGot,
|
|
[BaseTypeNames[LeftResolved.BaseType]],Bin.left)
|
|
else
|
|
RaiseMsg(20170216152234,nLeftSideOfIsOperatorExpectsAClassButGot,sLeftSideOfIsOperatorExpectsAClassButGot,
|
|
[LeftResolved.TypeEl.ElementTypeName],Bin.left);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ComputeBinaryExpr is-operator: left=',GetResolverResultDesc(LeftResolved),' right=',GetResolverResultDesc(RightResolved));
|
|
{$ENDIF}
|
|
RaiseMsg(20170216152236,nTypesAreNotRelated,sTypesAreNotRelated,[],Bin);
|
|
end;
|
|
eopAs:
|
|
begin
|
|
if (LeftResolved.TypeEl is TPasClassType) then
|
|
begin
|
|
if (LeftResolved.IdentEl=nil) or (LeftResolved.IdentEl is TPasType)
|
|
or (not (rrfReadable in LeftResolved.Flags)) then
|
|
RaiseMsg(20170216152237,nIllegalQualifier,sIllegalQualifier,['as'],Bin);
|
|
if RightResolved.IdentEl=nil then
|
|
RaiseXExpectedButYFound(20170216152630,'class',RightResolved.TypeEl.ElementTypeName,Bin.right);
|
|
if not (RightResolved.IdentEl is TPasType) then
|
|
RaiseXExpectedButYFound(20170216152632,'class',RightResolved.IdentEl.Name,Bin.right);
|
|
if (CheckSrcIsADstType(RightResolved,LeftResolved,Bin)<>cIncompatible) then
|
|
begin
|
|
SetResolverValueExpr(ResolvedEl,btContext,RightResolved.TypeEl,Bin,[rrfReadable]);
|
|
exit;
|
|
end;
|
|
RaiseMsg(20170216152239,nTypesAreNotRelated,sTypesAreNotRelated,[],Bin);
|
|
end;
|
|
end;
|
|
eopLessThan,eopGreaterThan, eopLessthanEqual,eopGreaterThanEqual:
|
|
if (LeftResolved.TypeEl.ClassType=TPasEnumType)
|
|
and (rrfReadable in LeftResolved.Flags)
|
|
and (LeftResolved.TypeEl=RightResolved.TypeEl)
|
|
and (rrfReadable in RightResolved.Flags)
|
|
then
|
|
begin
|
|
SetBaseType(btBoolean);
|
|
exit;
|
|
end;
|
|
eopSubIdent:
|
|
begin
|
|
ResolvedEl:=RightResolved;
|
|
exit;
|
|
end;
|
|
end
|
|
else if LeftResolved.BaseType=btSet then
|
|
begin
|
|
if (rrfReadable in LeftResolved.Flags)
|
|
and (RightResolved.BaseType=btSet)
|
|
and (rrfReadable in RightResolved.Flags) then
|
|
case Bin.OpCode of
|
|
eopAdd,
|
|
eopSubtract,
|
|
eopMultiply,
|
|
eopSymmetricaldifference,
|
|
eopLessthanEqual,
|
|
eopGreaterThanEqual:
|
|
begin
|
|
if RightResolved.TypeEl=nil then
|
|
begin
|
|
// right is empty set
|
|
if Bin.OpCode in [eopLessthanEqual,eopGreaterThanEqual] then
|
|
SetBaseType(btBoolean)
|
|
else
|
|
begin
|
|
ResolvedEl:=LeftResolved;
|
|
ResolvedEl.IdentEl:=nil;
|
|
ResolvedEl.ExprEl:=Bin;
|
|
end;
|
|
exit;
|
|
end
|
|
else if LeftResolved.TypeEl=nil then
|
|
begin
|
|
// left is empty set
|
|
if Bin.OpCode in [eopLessthanEqual,eopGreaterThanEqual] then
|
|
SetBaseType(btBoolean)
|
|
else
|
|
begin
|
|
ResolvedEl:=RightResolved;
|
|
ResolvedEl.IdentEl:=nil;
|
|
ResolvedEl.ExprEl:=Bin;
|
|
end;
|
|
exit;
|
|
end
|
|
else if (LeftResolved.SubType=RightResolved.SubType)
|
|
or ((LeftResolved.SubType in btAllBooleans)
|
|
and (RightResolved.SubType in btAllBooleans))
|
|
or ((LeftResolved.SubType in btAllInteger)
|
|
and (RightResolved.SubType in btAllInteger)) then
|
|
begin
|
|
// compatible set
|
|
if Bin.OpCode in [eopLessthanEqual,eopGreaterThanEqual] then
|
|
SetBaseType(btBoolean)
|
|
else
|
|
begin
|
|
ResolvedEl:=LeftResolved;
|
|
ResolvedEl.IdentEl:=nil;
|
|
ResolvedEl.ExprEl:=Bin;
|
|
end;
|
|
exit;
|
|
end;
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ComputeBinaryExpr + - * >< Sets LeftSubType='+BaseTypeNames[LeftResolved.SubType]
|
|
+' RightSubType='+BaseTypeNames[RightResolved.SubType]);
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
end
|
|
else if LeftResolved.BaseType=btModule then
|
|
begin
|
|
if Bin.OpCode=eopSubIdent then
|
|
begin
|
|
ResolvedEl:=RightResolved;
|
|
exit;
|
|
end;
|
|
end;
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ComputeBinaryExpr OpCode=',OpcodeStrings[Bin.OpCode],' Kind=',Bin.Kind,' Left=',GetResolverResultDesc(LeftResolved),' Right=',GetResolverResultDesc(RightResolved));
|
|
{$ENDIF}
|
|
RaiseMsg(20170216152241,nIllegalQualifier,sIllegalQualifier,[OpcodeStrings[Bin.OpCode]],Bin);
|
|
end;
|
|
|
|
procedure TPasResolver.ComputeArrayParams(Params: TParamsExpr; out
|
|
ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
|
|
StartEl: TPasElement);
|
|
|
|
procedure ComputeIndexProperty(Prop: TPasProperty);
|
|
begin
|
|
if [rcConstant,rcType]*Flags<>[] then
|
|
RaiseConstantExprExp(20170216152635,Params);
|
|
ComputeElement(GetPasPropertyType(Prop),ResolvedEl,[rcType],StartEl);
|
|
ResolvedEl.IdentEl:=Prop;
|
|
ResolvedEl.Flags:=[];
|
|
if GetPasPropertyGetter(Prop)<>nil then
|
|
Include(ResolvedEl.Flags,rrfReadable);
|
|
if GetPasPropertySetter(Prop)<>nil then
|
|
Include(ResolvedEl.Flags,rrfWritable);
|
|
end;
|
|
|
|
var
|
|
TypeEl: TPasType;
|
|
ClassScope: TPasClassScope;
|
|
ArrayEl: TPasArrayType;
|
|
ArgNo: Integer;
|
|
OrigResolved: TPasResolverResult;
|
|
SubParams: TParamsExpr;
|
|
begin
|
|
if Params.Value.CustomData is TResolvedReference then
|
|
begin
|
|
// e.g. Name[]
|
|
ComputeElement(Params.Value,ResolvedEl,
|
|
Flags-[rcNoImplicitProc,rcNoImplicitProcType],StartEl);
|
|
end
|
|
else if Params.Value.ClassType=TParamsExpr then
|
|
begin
|
|
SubParams:=TParamsExpr(Params.Value);
|
|
if SubParams.Kind in [pekArrayParams,pekFuncParams] then
|
|
begin
|
|
// e.g. Name()[] or Name[][]
|
|
ComputeElement(SubParams,ResolvedEl,
|
|
Flags-[rcNoImplicitProc,rcNoImplicitProcType],StartEl);
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20161010195646,SubParams);
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20160928174144,Params);
|
|
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ComputeArrayParams ResolvedEl=',GetResolverResultDesc(ResolvedEl));
|
|
{$ENDIF}
|
|
if ResolvedEl.BaseType in btAllStrings then
|
|
begin
|
|
// stringvar[] => char
|
|
if ResolvedEl.BaseType in [btWideString,btUnicodeString] then
|
|
ResolvedEl.BaseType:=btWideChar
|
|
else
|
|
ResolvedEl.BaseType:=btChar;
|
|
// keep ResolvedEl.IdentEl the string var
|
|
ResolvedEl.TypeEl:=FBaseTypes[ResolvedEl.BaseType];
|
|
ResolvedEl.ExprEl:=Params;
|
|
ResolvedEl.Flags:=ResolvedEl.Flags-[rrfWritable,rrfCanBeStatement]+[rrfAssignable];
|
|
end
|
|
else if (ResolvedEl.IdentEl is TPasProperty)
|
|
and (TPasProperty(ResolvedEl.IdentEl).Args.Count>0) then
|
|
// property with args
|
|
ComputeIndexProperty(TPasProperty(ResolvedEl.IdentEl))
|
|
else if ResolvedEl.BaseType=btContext then
|
|
begin
|
|
TypeEl:=ResolvedEl.TypeEl;
|
|
if TypeEl.ClassType=TPasClassType then
|
|
begin
|
|
ClassScope:=TypeEl.CustomData as TPasClassScope;
|
|
if ClassScope.DefaultProperty<>nil then
|
|
ComputeIndexProperty(ClassScope.DefaultProperty)
|
|
else
|
|
ComputeArrayParams_Class(Params,ResolvedEl,ClassScope,Flags,StartEl);
|
|
end
|
|
else if TypeEl.ClassType=TPasClassOfType then
|
|
begin
|
|
ClassScope:=TPasClassOfType(TypeEl).DestType.CustomData as TPasClassScope;
|
|
if ClassScope.DefaultProperty<>nil then
|
|
ComputeIndexProperty(ClassScope.DefaultProperty)
|
|
else
|
|
RaiseInternalError(20161010174916);
|
|
end
|
|
else if TypeEl.ClassType=TPasArrayType then
|
|
begin
|
|
ArrayEl:=TPasArrayType(TypeEl);
|
|
ArgNo:=0;
|
|
repeat
|
|
if length(ArrayEl.Ranges)=0 then
|
|
inc(ArgNo) // dynamic/open array has one dimension
|
|
else
|
|
inc(ArgNo,length(ArrayEl.Ranges)); // static array has several dimensions
|
|
if ArgNo>length(Params.Params) then
|
|
RaiseInternalError(20161010185535);
|
|
if ArgNo=length(Params.Params) then
|
|
break;
|
|
// continue in sub array
|
|
ArrayEl:=ResolveAliasType(ArrayEl.ElType) as TPasArrayType;
|
|
until false;
|
|
OrigResolved:=ResolvedEl;
|
|
ComputeElement(ArrayEl.ElType,ResolvedEl,Flags,StartEl);
|
|
// identifier and value is the array itself
|
|
ResolvedEl.IdentEl:=OrigResolved.IdentEl;
|
|
ResolvedEl.ExprEl:=OrigResolved.ExprEl;
|
|
ResolvedEl.Flags:=OrigResolved.Flags*[rrfReadable,rrfWritable];
|
|
if IsDynArray(ArrayEl) then
|
|
// dyn array elements are writable independent of the array
|
|
Include(ResolvedEl.Flags,rrfWritable);
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20161010151727,Params,GetResolverResultDesc(ResolvedEl));
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20160928174212,Params,GetResolverResultDesc(ResolvedEl));
|
|
end;
|
|
|
|
procedure TPasResolver.ComputeArrayParams_Class(Params: TParamsExpr;
|
|
var ResolvedEl: TPasResolverResult; ClassScope: TPasClassScope;
|
|
Flags: TPasResolverComputeFlags; StartEl: TPasElement);
|
|
begin
|
|
RaiseInternalError(20161010174916);
|
|
if Params=nil then ;
|
|
if ClassScope=nil then ;
|
|
if Flags=[] then ;
|
|
if StartEl=nil then ;
|
|
SetResolverIdentifier(ResolvedEl,btNone,nil,nil,[]);
|
|
end;
|
|
|
|
procedure TPasResolver.ComputeFuncParams(Params: TParamsExpr; out
|
|
ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
|
|
StartEl: TPasElement);
|
|
var
|
|
DeclEl: TPasElement;
|
|
BuiltInProc: TResElDataBuiltInProc;
|
|
Proc: TPasProcedure;
|
|
aClass: TPasClassType;
|
|
ResolvedTypeEl: TPasResolverResult;
|
|
Ref: TResolvedReference;
|
|
begin
|
|
if Params.Value.CustomData is TResolvedReference then
|
|
begin
|
|
Ref:=TResolvedReference(Params.Value.CustomData);
|
|
DeclEl:=Ref.Declaration;
|
|
if DeclEl.ClassType=TPasUnresolvedSymbolRef then
|
|
begin
|
|
if DeclEl.CustomData.ClassType=TResElDataBuiltInProc then
|
|
begin
|
|
BuiltInProc:=TResElDataBuiltInProc(DeclEl.CustomData);
|
|
if Assigned(BuiltInProc.GetCallResult) then
|
|
// built in function
|
|
BuiltInProc.GetCallResult(BuiltInProc,Params,ResolvedEl)
|
|
else
|
|
// built in procedure
|
|
SetResolverIdentifier(ResolvedEl,btProc,BuiltInProc.Proc,BuiltInProc.Proc,[]);
|
|
if bipfCanBeStatement in BuiltInProc.Flags then
|
|
Include(ResolvedEl.Flags,rrfCanBeStatement);
|
|
end
|
|
else if DeclEl.CustomData is TResElDataBaseType then
|
|
begin
|
|
// type cast to base type
|
|
if TResElDataBaseType(DeclEl.CustomData).BaseType=btCustom then
|
|
// custom base type
|
|
SetResolverValueExpr(ResolvedEl,
|
|
btCustom,
|
|
TPasUnresolvedSymbolRef(DeclEl),Params.Params[0],[rrfReadable])
|
|
else
|
|
SetResolverValueExpr(ResolvedEl,
|
|
TResElDataBaseType(DeclEl.CustomData).BaseType,
|
|
TPasUnresolvedSymbolRef(DeclEl),Params.Params[0],[rrfReadable]);
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20161006133040,Params,GetResolverResultDesc(ResolvedEl));
|
|
end
|
|
else
|
|
begin
|
|
// normal identifier (not built-in)
|
|
ComputeElement(DeclEl,ResolvedEl,Flags+[rcNoImplicitProc],StartEl);
|
|
if ResolvedEl.BaseType=btProc then
|
|
begin
|
|
if not (ResolvedEl.IdentEl is TPasProcedure) then
|
|
RaiseNotYetImplemented(20160928180201,Params,GetResolverResultDesc(ResolvedEl));
|
|
Proc:=TPasProcedure(ResolvedEl.IdentEl);
|
|
if rcConstant in Flags then
|
|
RaiseConstantExprExp(20170216152637,Params);
|
|
if Proc is TPasFunction then
|
|
// function call => return result
|
|
ComputeElement(TPasFunction(Proc).FuncType.ResultEl,ResolvedEl,
|
|
Flags+[rcNoImplicitProc],StartEl)
|
|
else if (Proc.ClassType=TPasConstructor)
|
|
and (rrfNewInstance in Ref.Flags) then
|
|
begin
|
|
// new instance call -> return value of type class
|
|
aClass:=GetReference_NewInstanceClass(Ref);
|
|
SetResolverValueExpr(ResolvedEl,btContext,aClass,Params.Value,[rrfReadable]);
|
|
end
|
|
else
|
|
// procedure call, result is neither readable nor writable
|
|
SetResolverIdentifier(ResolvedEl,btProc,Proc,Proc.ProcType,[]);
|
|
Include(ResolvedEl.Flags,rrfCanBeStatement);
|
|
end
|
|
else if ResolvedEl.TypeEl is TPasProcedureType then
|
|
begin
|
|
if Params.Value is TParamsExpr then
|
|
begin
|
|
// e.g. Name()() or Name[]()
|
|
Include(ResolvedEl.Flags,rrfReadable);
|
|
end;
|
|
if rrfReadable in ResolvedEl.Flags then
|
|
begin
|
|
// call procvar
|
|
if rcConstant in Flags then
|
|
RaiseConstantExprExp(20170216152639,Params);
|
|
if ResolvedEl.TypeEl is TPasFunctionType then
|
|
// function call => return result
|
|
ComputeElement(TPasFunctionType(ResolvedEl.TypeEl).ResultEl,
|
|
ResolvedEl,Flags+[rcNoImplicitProc],StartEl)
|
|
else
|
|
// procedure call, result is neither readable nor writable
|
|
SetResolverTypeExpr(ResolvedEl,btProc,TPasProcedureType(ResolvedEl.TypeEl),[]);
|
|
Include(ResolvedEl.Flags,rrfCanBeStatement);
|
|
end
|
|
else
|
|
begin
|
|
// typecast proctype
|
|
if length(Params.Params)<>1 then
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ComputeFuncParams DeclEl=',GetObjName(DeclEl),' ',GetResolverResultDbg(ResolvedEl));
|
|
{$ENDIF}
|
|
RaiseMsg(20170416185211,nWrongNumberOfParametersForTypeCast,
|
|
sWrongNumberOfParametersForTypeCast,[ResolvedEl.TypeEl.Name],Params);
|
|
end;
|
|
SetResolverValueExpr(ResolvedEl,btContext,TPasProcedureType(ResolvedEl.TypeEl),
|
|
Params.Params[0],[rrfReadable]);
|
|
end;
|
|
end
|
|
else if (DeclEl is TPasType) then
|
|
begin
|
|
// type cast
|
|
ResolvedTypeEl:=ResolvedEl;
|
|
ComputeElement(Params.Params[0],ResolvedEl,Flags,StartEl);
|
|
ResolvedEl.BaseType:=ResolvedTypeEl.BaseType;
|
|
ResolvedEl.TypeEl:=ResolvedTypeEl.TypeEl;
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20160928180048,Params,GetResolverResultDesc(ResolvedEl));
|
|
end;
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20160928174124,Params);
|
|
end;
|
|
|
|
procedure TPasResolver.ComputeSetParams(Params: TParamsExpr; out
|
|
ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
|
|
StartEl: TPasElement);
|
|
// [param,param,...]
|
|
var
|
|
ParamResolved, FirstResolved: TPasResolverResult;
|
|
i: Integer;
|
|
Param: TPasExpr;
|
|
begin
|
|
if length(Params.Params)=0 then
|
|
SetResolverValueExpr(ResolvedEl,btSet,nil,Params,[rrfReadable])
|
|
else
|
|
begin
|
|
FirstResolved:=Default(TPasResolverResult);
|
|
Flags:=Flags-[rcNoImplicitProc,rcNoImplicitProcType];
|
|
for i:=0 to length(Params.Params)-1 do
|
|
begin
|
|
Param:=Params.Params[i];
|
|
ComputeElement(Params.Params[0],ParamResolved,Flags,StartEl);
|
|
if ParamResolved.BaseType=btRange then
|
|
ConvertRangeToFirstValue(ParamResolved);
|
|
if FirstResolved.BaseType=btNone then
|
|
begin
|
|
// first value -> check type usable in a set
|
|
FirstResolved:=ParamResolved;
|
|
CheckIsOrdinal(FirstResolved,Param,true);
|
|
if not ResolvedElHasValue(FirstResolved) then
|
|
RaiseXExpectedButYFound(20170216152554,'ordinal value','type',Param);
|
|
end
|
|
else
|
|
begin
|
|
// next value
|
|
CheckSetElementsCompatible(Params.Params[0],Param,FirstResolved,ParamResolved);
|
|
end;
|
|
end;
|
|
|
|
FirstResolved.IdentEl:=nil;
|
|
if FirstResolved.ExprEl=nil then
|
|
FirstResolved.ExprEl:=Params;
|
|
FirstResolved.SubType:=FirstResolved.BaseType;
|
|
FirstResolved.BaseType:=btSet;
|
|
FirstResolved.Flags:=[rrfReadable];
|
|
ResolvedEl:=FirstResolved;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.CheckIsClass(El: TPasElement;
|
|
const ResolvedEl: TPasResolverResult);
|
|
begin
|
|
if (ResolvedEl.BaseType<>btContext) then
|
|
RaiseMsg(20170216152245,nXExpectedButYFound,sXExpectedButYFound,
|
|
['class',BaseTypeNames[ResolvedEl.BaseType]],El);
|
|
if (ResolvedEl.TypeEl.ClassType<>TPasClassType) then
|
|
RaiseMsg(20170216152246,nXExpectedButYFound,sXExpectedButYFound,
|
|
['class',ResolvedEl.TypeEl.ElementTypeName],El);
|
|
end;
|
|
|
|
function TPasResolver.CheckTypeCastClassInstanceToClass(const FromClassRes,
|
|
ToClassRes: TPasResolverResult; ErrorEl: TPasElement): integer;
|
|
// called when type casting a class instance into an unrelated class
|
|
begin
|
|
if FromClassRes.BaseType=btNone then ;
|
|
if ToClassRes.BaseType=btNone then ;
|
|
if ErrorEl=nil then ;
|
|
Result:=cIncompatible;
|
|
end;
|
|
|
|
procedure TPasResolver.CheckRangeExpr(Left, Right: TPasExpr; out LeftResolved,
|
|
RightResolved: TPasResolverResult);
|
|
begin
|
|
ComputeElement(Left,LeftResolved,[rcSkipTypeAlias,rcConstant]);
|
|
ComputeElement(Right,RightResolved,[rcSkipTypeAlias,rcConstant]);
|
|
CheckSetElementsCompatible(Left,Right,LeftResolved,RightResolved);
|
|
end;
|
|
|
|
procedure TPasResolver.CheckSetElementsCompatible(Left, Right: TPasExpr;
|
|
const LeftResolved, RightResolved: TPasResolverResult);
|
|
begin
|
|
// check both are values
|
|
if not ResolvedElHasValue(LeftResolved) then
|
|
begin
|
|
if LeftResolved.TypeEl<>nil then
|
|
RaiseXExpectedButYFound(20170216152645,'ordinal',LeftResolved.TypeEl.ElementTypeName,Left)
|
|
else
|
|
RaiseXExpectedButYFound(20170216152648,'ordinal',BaseTypeNames[LeftResolved.BaseType],Left);
|
|
end;
|
|
if not ResolvedElHasValue(RightResolved) then
|
|
begin
|
|
if RightResolved.TypeEl<>nil then
|
|
RaiseXExpectedButYFound(20170216152651,'ordinal',RightResolved.TypeEl.ElementTypeName,Right)
|
|
else
|
|
RaiseXExpectedButYFound(20170216152653,'ordinal',BaseTypeNames[RightResolved.BaseType],Right);
|
|
end;
|
|
// check both have the same ordinal type
|
|
if LeftResolved.BaseType in btAllBooleans then
|
|
begin
|
|
if (RightResolved.BaseType in btAllBooleans) then
|
|
exit;
|
|
RaiseXExpectedButYFound(20170216152656,'boolean',BaseTypeNames[RightResolved.BaseType],Right);
|
|
end
|
|
else if LeftResolved.BaseType in btAllInteger then
|
|
begin
|
|
if (RightResolved.BaseType in btAllInteger) then
|
|
exit;
|
|
RaiseXExpectedButYFound(20170216152658,'integer',BaseTypeNames[RightResolved.BaseType],Right);
|
|
end
|
|
else if LeftResolved.BaseType=btChar then
|
|
begin
|
|
if (RightResolved.BaseType=btChar) then
|
|
exit;
|
|
RaiseXExpectedButYFound(20170216152702,'char',BaseTypeNames[RightResolved.BaseType],Right);
|
|
end
|
|
else if LeftResolved.BaseType=btContext then
|
|
begin
|
|
if LeftResolved.TypeEl.ClassType=TPasEnumType then
|
|
begin
|
|
if LeftResolved.TypeEl=RightResolved.TypeEl then
|
|
exit;
|
|
if RightResolved.TypeEl.ClassType<>TPasEnumType then
|
|
RaiseXExpectedButYFound(20170216152707,LeftResolved.TypeEl.Parent.Name,RightResolved.TypeEl.ElementTypeName,Right);
|
|
if LeftResolved.TypeEl.Parent<>RightResolved.TypeEl.Parent then
|
|
RaiseXExpectedButYFound(20170216152710,LeftResolved.TypeEl.Parent.Name,RightResolved.TypeEl.Parent.Name,Right);
|
|
end
|
|
else
|
|
RaiseXExpectedButYFound(20170216152712,'ordinal',BaseTypeNames[LeftResolved.BaseType],Left);
|
|
end
|
|
else
|
|
RaiseXExpectedButYFound(20170216152714,'ordinal',BaseTypeNames[LeftResolved.BaseType],Left);
|
|
end;
|
|
|
|
function TPasResolver.CheckIsOrdinal(
|
|
const ResolvedEl: TPasResolverResult; ErrorEl: TPasElement;
|
|
RaiseOnError: boolean): boolean;
|
|
begin
|
|
Result:=false;
|
|
if ResolvedEl.BaseType in (btAllInteger+btAllBooleans+[btChar]) then
|
|
else if (ResolvedEl.BaseType=btContext) then
|
|
begin
|
|
if ResolvedEl.TypeEl.ClassType=TPasEnumType then
|
|
else if RaiseOnError then
|
|
RaiseXExpectedButYFound(20170216152718,'ordinal value',ResolvedEl.TypeEl.ElementTypeName,ErrorEl)
|
|
else
|
|
exit;
|
|
end
|
|
else if RaiseOnError then
|
|
RaiseXExpectedButYFound(20170216152720,'ordinal value',BaseTypeNames[ResolvedEl.BaseType],ErrorEl)
|
|
else
|
|
exit;
|
|
Result:=true;
|
|
end;
|
|
|
|
procedure TPasResolver.ConvertRangeToFirstValue(
|
|
var ResolvedEl: TPasResolverResult);
|
|
begin
|
|
if ResolvedEl.BaseType<>btRange then
|
|
RaiseInternalError(20161001155732);
|
|
if ResolvedEl.TypeEl=nil then
|
|
if ResolvedEl.IdentEl<>nil then
|
|
RaiseNotYetImplemented(20161001155747,ResolvedEl.IdentEl)
|
|
else
|
|
RaiseNotYetImplemented(20161001155834,ResolvedEl.ExprEl);
|
|
ResolvedEl.BaseType:=ResolvedEl.SubType;
|
|
ResolvedEl.SubType:=btNone;
|
|
end;
|
|
|
|
function TPasResolver.IsCharLiteral(const Value: string): boolean;
|
|
var
|
|
p: PChar;
|
|
begin
|
|
Result:=false;
|
|
p:=PChar(Value);
|
|
if (p^='''') then
|
|
begin
|
|
inc(p);
|
|
if p^ in [#32..#196] then
|
|
begin
|
|
inc(p);
|
|
if p^='''' then
|
|
exit(true);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TPasResolver.CheckBuiltInMinParamCount(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; MinCount: integer; RaiseOnError: boolean): boolean;
|
|
begin
|
|
if (not (Expr is TParamsExpr)) or (length(TParamsExpr(Expr).Params)<MinCount) then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170216152248,nWrongNumberOfParametersForCallTo,
|
|
sWrongNumberOfParametersForCallTo,[Proc.Signature],Expr);
|
|
exit(false);
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TPasResolver.CheckBuiltInMaxParamCount(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr; MaxCount: integer; RaiseOnError: boolean): integer;
|
|
begin
|
|
if length(Params.Params)>MaxCount then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170329154348,nWrongNumberOfParametersForCallTo,
|
|
sWrongNumberOfParametersForCallTo,[Proc.Signature],Params.Params[MaxCount]);
|
|
exit(cIncompatible);
|
|
end;
|
|
|
|
Result:=cExact;
|
|
end;
|
|
|
|
function TPasResolver.CheckRaiseTypeArgNo(id: int64; ArgNo: integer;
|
|
Param: TPasExpr; const ParamResolved: TPasResolverResult; Expected: string;
|
|
RaiseOnError: boolean): integer;
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(id,nIncompatibleTypeArgNo,sIncompatibleTypeArgNo,
|
|
[IntToStr(ArgNo),GetResolverResultDescription(ParamResolved,true),Expected],Param);
|
|
Result:=cIncompatible;
|
|
end;
|
|
|
|
function TPasResolver.CheckAssignCompatibilityCustom(const LHS,
|
|
RHS: TPasResolverResult; ErrorEl: TPasElement; RaiseOnIncompatible: boolean;
|
|
var Handled: boolean): integer;
|
|
// called when LHS or RHS BaseType is btCustom
|
|
// if RaiseOnIncompatible=true you can raise an useful error.
|
|
begin
|
|
Result:=cIncompatible;
|
|
if LHS.BaseType=btNone then ;
|
|
if RHS.BaseType=btNone then ;
|
|
if ErrorEl=nil then ;
|
|
if RaiseOnIncompatible then ;
|
|
if Handled then ;
|
|
end;
|
|
|
|
function TPasResolver.CheckEqualCompatibilityCustomType(const LHS,
|
|
RHS: TPasResolverResult; ErrorEl: TPasElement; RaiseOnIncompatible: boolean
|
|
): integer;
|
|
begin
|
|
Result:=cIncompatible;
|
|
if LHS.BaseType=RHS.BaseType then;
|
|
if ErrorEl=nil then;
|
|
if RaiseOnIncompatible then ;
|
|
end;
|
|
|
|
function TPasResolver.BI_Length_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
// check params of built in proc 'length'
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
ParamResolved: TPasResolverResult;
|
|
begin
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,1,RaiseOnError) then
|
|
exit(cIncompatible);
|
|
Params:=TParamsExpr(Expr);
|
|
|
|
// first param: string or dynamic array
|
|
Param:=Params.Params[0];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
Result:=cIncompatible;
|
|
if rrfReadable in ParamResolved.Flags then
|
|
begin
|
|
if ParamResolved.BaseType in btAllStringAndChars then
|
|
Result:=cExact
|
|
else if ParamResolved.BaseType=btContext then
|
|
begin
|
|
if (ParamResolved.TypeEl.ClassType=TPasArrayType) then
|
|
Result:=cExact;
|
|
end;
|
|
end;
|
|
if Result=cIncompatible then
|
|
exit(CheckRaiseTypeArgNo(20170329160335,1,Param,ParamResolved,
|
|
'string or array',RaiseOnError));
|
|
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,1,RaiseOnError);
|
|
end;
|
|
|
|
procedure TPasResolver.BI_Length_OnGetCallResult(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr; out ResolvedEl: TPasResolverResult);
|
|
begin
|
|
if Params=nil then ;
|
|
SetResolverIdentifier(ResolvedEl,btInt64,Proc.Proc,FBaseTypes[btInt64],[rrfReadable]);
|
|
end;
|
|
|
|
function TPasResolver.BI_SetLength_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
// check params of built in proc 'setlength'
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
ParamResolved: TPasResolverResult;
|
|
begin
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,2,RaiseOnError) then
|
|
exit(cIncompatible);
|
|
Params:=TParamsExpr(Expr);
|
|
|
|
// first param: string or array variable
|
|
Param:=Params.Params[0];
|
|
ComputeElement(Param,ParamResolved,[rcNoImplicitProc]);
|
|
Result:=cIncompatible;
|
|
if ResolvedElCanBeVarParam(ParamResolved) then
|
|
begin
|
|
if ParamResolved.BaseType in btAllStrings then
|
|
Result:=cExact
|
|
else if ParamResolved.BaseType=btContext then
|
|
begin
|
|
if IsDynArray(ParamResolved.TypeEl) then
|
|
Result:=cExact;
|
|
end;
|
|
end;
|
|
if Result=cIncompatible then
|
|
exit(CheckRaiseTypeArgNo(20170216152250,1,Param,ParamResolved,
|
|
'string or dynamic array variable',RaiseOnError));
|
|
|
|
// second param: new length
|
|
Param:=Params.Params[1];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
Result:=cIncompatible;
|
|
if (rrfReadable in ParamResolved.Flags)
|
|
and (ParamResolved.BaseType in btAllInteger) then
|
|
Result:=cExact;
|
|
if Result=cIncompatible then
|
|
exit(CheckRaiseTypeArgNo(20170329160338,2,Param,ParamResolved,
|
|
'integer',RaiseOnError));
|
|
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,2,RaiseOnError);
|
|
end;
|
|
|
|
procedure TPasResolver.BI_SetLength_OnFinishParamsExpr(
|
|
Proc: TResElDataBuiltInProc; Params: TParamsExpr);
|
|
var
|
|
P: TPasExprArray;
|
|
begin
|
|
if Proc=nil then ;
|
|
P:=Params.Params;
|
|
AccessExpr(P[0],rraVarParam);
|
|
AccessExpr(P[1],rraRead);
|
|
end;
|
|
|
|
function TPasResolver.BI_InExclude_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
// check params of built in proc 'include'
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
ParamResolved: TPasResolverResult;
|
|
EnumType: TPasEnumType;
|
|
begin
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,2,RaiseOnError) then
|
|
exit(cIncompatible);
|
|
Params:=TParamsExpr(Expr);
|
|
|
|
// first param: variable of set of enumtype
|
|
Param:=Params.Params[0];
|
|
ComputeElement(Param,ParamResolved,[rcNoImplicitProc]);
|
|
EnumType:=nil;
|
|
if ([rrfReadable,rrfWritable]*ParamResolved.Flags=[rrfReadable,rrfWritable])
|
|
and ((ParamResolved.IdentEl is TPasVariable)
|
|
or (ParamResolved.IdentEl is TPasArgument)) then
|
|
begin
|
|
if (ParamResolved.BaseType=btSet)
|
|
and (ParamResolved.TypeEl is TPasEnumType) then
|
|
EnumType:=TPasEnumType(ParamResolved.TypeEl);
|
|
end;
|
|
if EnumType=nil then
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnGetCallCompatibility_InExclude ',GetResolverResultDesc(ParamResolved));
|
|
{$ENDIF}
|
|
exit(CheckRaiseTypeArgNo(20170216152301,1,Param,ParamResolved,
|
|
'variable of set of enumtype',RaiseOnError));
|
|
end;
|
|
|
|
// second param: enum
|
|
Param:=Params.Params[1];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
if (not (rrfReadable in ParamResolved.Flags))
|
|
or (ParamResolved.TypeEl<>EnumType) then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseIncompatibleType(20170216152302,nIncompatibleTypeArgNo,
|
|
['2'],ParamResolved.TypeEl,EnumType,Param);
|
|
exit(cIncompatible);
|
|
end;
|
|
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,2,RaiseOnError);
|
|
end;
|
|
|
|
procedure TPasResolver.BI_InExclude_OnFinishParamsExpr(
|
|
Proc: TResElDataBuiltInProc; Params: TParamsExpr);
|
|
var
|
|
P: TPasExprArray;
|
|
begin
|
|
if Proc=nil then ;
|
|
P:=Params.Params;
|
|
AccessExpr(P[0],rraVarParam);
|
|
AccessExpr(P[1],rraRead);
|
|
end;
|
|
|
|
function TPasResolver.BI_Break_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
var
|
|
Params: TParamsExpr;
|
|
begin
|
|
if GetLoop(Expr)=nil then
|
|
RaiseMsg(20170216152306,nMustBeInsideALoop,sMustBeInsideALoop,['Break'],Expr);
|
|
if (not (Expr is TParamsExpr)) or (length(TParamsExpr(Expr).Params)=0) then
|
|
exit(cExact);
|
|
Params:=TParamsExpr(Expr);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnGetCallCompatibility_Break Params=',length(Params.Params));
|
|
{$ENDIF}
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,0,RaiseOnError);
|
|
end;
|
|
|
|
function TPasResolver.BI_Continue_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
var
|
|
Params: TParamsExpr;
|
|
begin
|
|
if GetLoop(Expr)=nil then
|
|
RaiseMsg(20170216152309,nMustBeInsideALoop,sMustBeInsideALoop,['Continue'],Expr);
|
|
if (not (Expr is TParamsExpr)) or (length(TParamsExpr(Expr).Params)=0) then
|
|
exit(cExact);
|
|
Params:=TParamsExpr(Expr);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnGetCallCompatibility_Continue Params=',length(Params.Params));
|
|
{$ENDIF}
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,0,RaiseOnError);
|
|
end;
|
|
|
|
function TPasResolver.BI_Exit_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
ParamResolved, ResultResolved: TPasResolverResult;
|
|
i: Integer;
|
|
ProcScope: TPasProcedureScope;
|
|
ResultEl: TPasResultElement;
|
|
Flags: TPasResolverComputeFlags;
|
|
begin
|
|
if (not (Expr is TParamsExpr)) or (length(TParamsExpr(Expr).Params)=0) then
|
|
exit(cExact);
|
|
Params:=TParamsExpr(Expr);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnGetCallCompatibility_Exit Params=',length(Params.Params));
|
|
{$ENDIF}
|
|
|
|
// first param: result
|
|
Param:=Params.Params[0];
|
|
Result:=cIncompatible;
|
|
i:=ScopeCount-1;
|
|
while (i>0) and (not (Scopes[i] is TPasProcedureScope)) do dec(i);
|
|
if i>0 then
|
|
begin
|
|
// first param is function result
|
|
ProcScope:=TPasProcedureScope(Scopes[i]);
|
|
if not (ProcScope.Element is TPasFunction) then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170216152312,nWrongNumberOfParametersForCallTo,
|
|
sWrongNumberOfParametersForCallTo,['procedure exit'],Params.Params[0]);
|
|
exit(cIncompatible);
|
|
end;
|
|
ResultEl:=(ProcScope.Element as TPasFunction).FuncType.ResultEl;
|
|
ComputeElement(ResultEl,ResultResolved,[rcType]);
|
|
end
|
|
else
|
|
begin
|
|
// default: main program, param is an integer
|
|
SetResolverTypeExpr(ResultResolved,btLongint,FBaseTypes[btLongint],[rrfReadable,rrfWritable]);
|
|
end;
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnGetCallCompatibility_Exit ResultResolved=',GetResolverResultDesc(ResultResolved));
|
|
{$ENDIF}
|
|
|
|
Flags:=[];
|
|
if IsProcedureType(ResultResolved,true) then
|
|
Include(Flags,rcNoImplicitProc);
|
|
ComputeElement(Param,ParamResolved,Flags);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnGetCallCompatibility_Exit ParamResolved=',GetResolverResultDesc(ParamResolved));
|
|
{$ENDIF}
|
|
|
|
if rrfReadable in ParamResolved.Flags then
|
|
Result:=CheckAssignResCompatibility(ResultResolved,ParamResolved,Param,false);
|
|
if Result=cIncompatible then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseIncompatibleTypeRes(20170216152314,nIncompatibleTypeArgNo,
|
|
['1'],ParamResolved,ResultResolved,Param);
|
|
exit;
|
|
end;
|
|
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,1,RaiseOnError);
|
|
end;
|
|
|
|
function TPasResolver.BI_IncDec_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
ParamResolved, IncrResolved: TPasResolverResult;
|
|
begin
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,1,RaiseOnError) then
|
|
exit(cIncompatible);
|
|
Params:=TParamsExpr(Expr);
|
|
|
|
// first param: var Integer
|
|
Param:=Params.Params[0];
|
|
ComputeElement(Param,ParamResolved,[rcNoImplicitProc]);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.OnGetCallCompatibility_IncDec ParamResolved=',GetResolverResultDesc(ParamResolved));
|
|
{$ENDIF}
|
|
Result:=cIncompatible;
|
|
// Expr must be a variable
|
|
if not ResolvedElCanBeVarParam(ParamResolved) then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170216152319,nVariableIdentifierExpected,sVariableIdentifierExpected,[],Expr);
|
|
exit;
|
|
end;
|
|
if ParamResolved.BaseType in btAllInteger then
|
|
Result:=cExact;
|
|
if Result=cIncompatible then
|
|
exit(CheckRaiseTypeArgNo(20170216152320,1,Param,ParamResolved,'integer',RaiseOnError));
|
|
|
|
if length(Params.Params)=1 then
|
|
exit;
|
|
|
|
// second param: increment/decrement
|
|
Param:=Params.Params[1];
|
|
ComputeElement(Param,IncrResolved,[]);
|
|
Result:=cIncompatible;
|
|
if rrfReadable in IncrResolved.Flags then
|
|
begin
|
|
if IncrResolved.BaseType in btAllInteger then
|
|
Result:=cExact;
|
|
end;
|
|
if Result=cIncompatible then
|
|
exit(CheckRaiseTypeArgNo(20170216152322,2,Param,IncrResolved,'integer',RaiseOnError));
|
|
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,2,RaiseOnError);
|
|
end;
|
|
|
|
procedure TPasResolver.BI_IncDec_OnFinishParamsExpr(
|
|
Proc: TResElDataBuiltInProc; Params: TParamsExpr);
|
|
var
|
|
P: TPasExprArray;
|
|
begin
|
|
if Proc=nil then ;
|
|
P:=Params.Params;
|
|
AccessExpr(P[0],rraVarParam);
|
|
if Length(P)>1 then
|
|
AccessExpr(P[1],rraRead);
|
|
end;
|
|
|
|
function TPasResolver.BI_Assigned_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
// check params of built in proc 'Assigned'
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
ParamResolved: TPasResolverResult;
|
|
C: TClass;
|
|
begin
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,1,RaiseOnError) then
|
|
exit(cIncompatible);
|
|
Params:=TParamsExpr(Expr);
|
|
|
|
// first param: pointer, class, class instance, proc type or array
|
|
Param:=Params.Params[0];
|
|
ComputeElement(Param,ParamResolved,[rcNoImplicitProcType]);
|
|
Result:=cIncompatible;
|
|
if ParamResolved.BaseType in [btNil,btPointer] then
|
|
Result:=cExact
|
|
else if (ParamResolved.BaseType=btContext) then
|
|
begin
|
|
C:=ParamResolved.TypeEl.ClassType;
|
|
if (C=TPasClassType)
|
|
or (C=TPasClassOfType)
|
|
or C.InheritsFrom(TPasProcedureType)
|
|
or ((C=TPasArrayType) and (length(TPasArrayType(ParamResolved.TypeEl).Ranges)=0)) then
|
|
Result:=cExact;
|
|
end;
|
|
if Result=cIncompatible then
|
|
exit(CheckRaiseTypeArgNo(20170216152329,1,Param,ParamResolved,'class or array',RaiseOnError));
|
|
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,1,RaiseOnError);
|
|
end;
|
|
|
|
procedure TPasResolver.BI_Assigned_OnGetCallResult(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr; out ResolvedEl: TPasResolverResult);
|
|
begin
|
|
SetResolverIdentifier(ResolvedEl,btBoolean,Proc.Proc,FBaseTypes[btBoolean],[rrfReadable]);
|
|
end;
|
|
|
|
function TPasResolver.BI_Chr_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
ParamResolved: TPasResolverResult;
|
|
begin
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,1,RaiseOnError) then
|
|
exit(cIncompatible);
|
|
Params:=TParamsExpr(Expr);
|
|
|
|
// first param: integer
|
|
Param:=Params.Params[0];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
Result:=cIncompatible;
|
|
if rrfReadable in ParamResolved.Flags then
|
|
begin
|
|
if ParamResolved.BaseType in btAllInteger then
|
|
Result:=cExact;
|
|
end;
|
|
if Result=cIncompatible then
|
|
exit(CheckRaiseTypeArgNo(20170325185321,1,Param,ParamResolved,'integer',RaiseOnError));
|
|
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,1,RaiseOnError);
|
|
end;
|
|
|
|
procedure TPasResolver.BI_Chr_OnGetCallResult(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr; out ResolvedEl: TPasResolverResult);
|
|
begin
|
|
SetResolverIdentifier(ResolvedEl,btChar,Proc.Proc,FBaseTypes[btChar],[rrfReadable]);
|
|
end;
|
|
|
|
function TPasResolver.BI_Ord_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
|
|
Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
ParamResolved: TPasResolverResult;
|
|
begin
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,1,RaiseOnError) then
|
|
exit(cIncompatible);
|
|
Params:=TParamsExpr(Expr);
|
|
|
|
// first param: enum or char
|
|
Param:=Params.Params[0];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
Result:=cIncompatible;
|
|
if rrfReadable in ParamResolved.Flags then
|
|
begin
|
|
if ParamResolved.BaseType=btChar then
|
|
Result:=cExact
|
|
else if (ParamResolved.BaseType=btContext) and (ParamResolved.TypeEl is TPasEnumType) then
|
|
Result:=cExact;
|
|
end;
|
|
if Result=cIncompatible then
|
|
exit(CheckRaiseTypeArgNo(20170216152334,1,Param,ParamResolved,'enum or char',RaiseOnError));
|
|
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,1,RaiseOnError);
|
|
end;
|
|
|
|
procedure TPasResolver.BI_Ord_OnGetCallResult(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr; out ResolvedEl: TPasResolverResult);
|
|
begin
|
|
SetResolverIdentifier(ResolvedEl,btSmallInt,Proc.Proc,FBaseTypes[btSmallInt],[rrfReadable]);
|
|
end;
|
|
|
|
function TPasResolver.BI_LowHigh_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
// check params of built in proc 'Low' or 'High'
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
ParamResolved: TPasResolverResult;
|
|
TypeEl: TPasType;
|
|
begin
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,1,RaiseOnError) then
|
|
exit(cIncompatible);
|
|
Params:=TParamsExpr(Expr);
|
|
|
|
// first param: enum, range or char
|
|
Param:=Params.Params[0];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
Result:=cIncompatible;
|
|
if CheckIsOrdinal(ParamResolved,Param,false) then
|
|
Result:=cExact
|
|
else if ParamResolved.BaseType=btSet then
|
|
Result:=cExact
|
|
else if (ParamResolved.BaseType=btContext) then
|
|
begin
|
|
TypeEl:=ParamResolved.TypeEl;
|
|
if (TypeEl.ClassType=TPasArrayType)
|
|
or (TypeEl.ClassType=TPasSetType) then
|
|
Result:=cExact;
|
|
end;
|
|
if Result=cIncompatible then
|
|
exit(CheckRaiseTypeArgNo(20170216152338,1,Param,ParamResolved,'enum or char',RaiseOnError));
|
|
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,1,RaiseOnError);
|
|
end;
|
|
|
|
procedure TPasResolver.BI_LowHigh_OnGetCallResult(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr; out ResolvedEl: TPasResolverResult);
|
|
var
|
|
ArrayEl: TPasArrayType;
|
|
Param: TPasExpr;
|
|
TypeEl: TPasType;
|
|
begin
|
|
Param:=Params.Params[0];
|
|
ComputeElement(Param,ResolvedEl,[]);
|
|
if ResolvedEl.BaseType=btContext then
|
|
begin
|
|
TypeEl:=ResolvedEl.TypeEl;
|
|
if TypeEl.ClassType=TPasArrayType then
|
|
begin
|
|
// array: result type is type of first dimension
|
|
ArrayEl:=TPasArrayType(TypeEl);
|
|
if length(ArrayEl.Ranges)=0 then
|
|
SetResolverIdentifier(ResolvedEl,btInt64,Proc.Proc,FBaseTypes[btInt64],[rrfReadable])
|
|
else
|
|
begin
|
|
ComputeElement(ArrayEl.Ranges[0],ResolvedEl,[rcConstant]);
|
|
if ResolvedEl.BaseType=btRange then
|
|
ConvertRangeToFirstValue(ResolvedEl);
|
|
end;
|
|
end
|
|
else if TypeEl.ClassType=TPasSetType then
|
|
begin
|
|
ResolvedEl.TypeEl:=TPasSetType(TypeEl).EnumType;
|
|
end;
|
|
end
|
|
else if ResolvedEl.BaseType=btSet then
|
|
begin
|
|
ResolvedEl.BaseType:=ResolvedEl.SubType;
|
|
ResolvedEl.SubType:=btNone;
|
|
end
|
|
else
|
|
;// ordinal: result type is argument type
|
|
ResolvedEl.Flags:=ResolvedEl.Flags-[rrfWritable]+[rrfReadable];
|
|
end;
|
|
|
|
function TPasResolver.BI_PredSucc_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
// check params of built in proc 'Pred' or 'Succ'
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
ParamResolved: TPasResolverResult;
|
|
begin
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,1,RaiseOnError) then
|
|
exit(cIncompatible);
|
|
Params:=TParamsExpr(Expr);
|
|
|
|
// first param: enum, range, set, char or integer
|
|
Param:=Params.Params[0];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
Result:=cIncompatible;
|
|
if CheckIsOrdinal(ParamResolved,Param,false) then
|
|
Result:=cExact;
|
|
if Result=cIncompatible then
|
|
exit(CheckRaiseTypeArgNo(20170216152343,1,Param,ParamResolved,'ordinal',RaiseOnError));
|
|
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,1,RaiseOnError);
|
|
end;
|
|
|
|
procedure TPasResolver.BI_PredSucc_OnGetCallResult(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr; out ResolvedEl: TPasResolverResult);
|
|
begin
|
|
ComputeElement(Params.Params[0],ResolvedEl,[]);
|
|
ResolvedEl.Flags:=ResolvedEl.Flags-[rrfWritable];
|
|
end;
|
|
|
|
function TPasResolver.BI_Str_CheckParam(IsFunc: boolean; Param: TPasExpr;
|
|
const ParamResolved: TPasResolverResult; ArgNo: integer; RaiseOnError: boolean
|
|
): integer;
|
|
|
|
function CheckFormat(FormatExpr: TPasExpr; Index: integer;
|
|
const ParamResolved: TPasResolverResult): boolean;
|
|
var
|
|
ResolvedEl: TPasResolverResult;
|
|
Ok: Boolean;
|
|
begin
|
|
if FormatExpr=nil then exit(true);
|
|
Result:=false;
|
|
Ok:=false;
|
|
if ParamResolved.BaseType in btAllFloats then
|
|
// floats supports value:Width:Precision
|
|
Ok:=true
|
|
else
|
|
// all other only support only Width
|
|
Ok:=Index<2;
|
|
if not Ok then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170319222319,nIllegalExpression,sIllegalExpression,[],FormatExpr);
|
|
exit;
|
|
end;
|
|
ComputeElement(FormatExpr,ResolvedEl,[]);
|
|
if not (ResolvedEl.BaseType in btAllInteger) then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170319221515,nXExpectedButYFound,sXExpectedButYFound,
|
|
['integer',GetResolverResultDescription(ResolvedEl,true)],FormatExpr);
|
|
exit;
|
|
end;
|
|
if not (rrfReadable in ResolvedEl.Flags) then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170319221755,nNotReadable,sNotReadable,[],FormatExpr);
|
|
exit;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
var
|
|
TypeEl: TPasType;
|
|
begin
|
|
Result:=cIncompatible;
|
|
if ParamResolved.BaseType in (btAllInteger+btAllBooleans+btAllFloats) then
|
|
Result:=cExact
|
|
else if IsFunc and (ParamResolved.BaseType in btAllStringAndChars) then
|
|
Result:=cExact
|
|
else if ParamResolved.BaseType=btContext then
|
|
begin
|
|
TypeEl:=ParamResolved.TypeEl;
|
|
if TypeEl.ClassType=TPasEnumType then
|
|
Result:=cExact
|
|
end;
|
|
if Result=cIncompatible then
|
|
exit(CheckRaiseTypeArgNo(20170319220517,ArgNo,Param,ParamResolved,'boolean, integer, enum value',RaiseOnError));
|
|
if not CheckFormat(Param.format1,1,ParamResolved) then
|
|
exit(cIncompatible);
|
|
if not CheckFormat(Param.format2,2,ParamResolved) then
|
|
exit(cIncompatible);
|
|
end;
|
|
|
|
function TPasResolver.BI_StrProc_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
// check params of built-in procedure 'Str'
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
ParamResolved: TPasResolverResult;
|
|
begin
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,2,RaiseOnError) then
|
|
exit(cIncompatible);
|
|
Params:=TParamsExpr(Expr);
|
|
if ParentNeedsExprResult(Params) then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170326084331,nIncompatibleTypesGotExpected,
|
|
sIncompatibleTypesGotExpected,['procedure str','function str'],Params);
|
|
exit(cIncompatible);
|
|
end;
|
|
|
|
// first param: boolean, integer, enum, class instance
|
|
Param:=Params.Params[0];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
Result:=BI_Str_CheckParam(false,Param,ParamResolved,1,RaiseOnError);
|
|
if Result=cIncompatible then
|
|
exit;
|
|
|
|
// second parameter: string variable
|
|
Param:=Params.Params[1];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
Result:=cIncompatible;
|
|
if ResolvedElCanBeVarParam(ParamResolved) then
|
|
begin
|
|
if ParamResolved.BaseType in btAllStrings then
|
|
Result:=cExact;
|
|
end;
|
|
if Result=cIncompatible then
|
|
exit(CheckRaiseTypeArgNo(20170319220806,1,Param,ParamResolved,'string variable',RaiseOnError));
|
|
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,2,RaiseOnError);
|
|
end;
|
|
|
|
procedure TPasResolver.BI_StrProc_OnFinishParamsExpr(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr);
|
|
var
|
|
P: TPasExprArray;
|
|
begin
|
|
if Proc=nil then ;
|
|
P:=Params.Params;
|
|
AccessExpr(P[0],rraRead);
|
|
AccessExpr(P[1],rraVarParam);
|
|
end;
|
|
|
|
function TPasResolver.BI_StrFunc_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
ParamResolved: TPasResolverResult;
|
|
i: Integer;
|
|
begin
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,1,RaiseOnError) then
|
|
exit(cIncompatible);
|
|
Params:=TParamsExpr(Expr);
|
|
if not ParentNeedsExprResult(Params) then
|
|
begin
|
|
// not in an expression -> the 'procedure str' is needed, not the 'function str'
|
|
if RaiseOnError then
|
|
RaiseMsg(20170326084622,nIncompatibleTypesGotExpected,
|
|
sIncompatibleTypesGotExpected,['function str','procedure str'],Params);
|
|
exit(cIncompatible);
|
|
end;
|
|
|
|
// param: string, boolean, integer, enum, class instance
|
|
for i:=0 to length(Params.Params)-1 do
|
|
begin
|
|
Param:=Params.Params[i];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
Result:=BI_Str_CheckParam(true,Param,ParamResolved,i+1,RaiseOnError);
|
|
if Result=cIncompatible then
|
|
exit;
|
|
end;
|
|
|
|
Result:=cExact;
|
|
end;
|
|
|
|
procedure TPasResolver.BI_StrFunc_OnGetCallResult(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr; out ResolvedEl: TPasResolverResult);
|
|
begin
|
|
if Params=nil then ;
|
|
SetResolverIdentifier(ResolvedEl,btString,Proc.Proc,FBaseTypes[btString],[rrfReadable]);
|
|
end;
|
|
|
|
function TPasResolver.BI_ConcatArray_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
ParamResolved, ElTypeResolved, FirstElTypeResolved: TPasResolverResult;
|
|
i: Integer;
|
|
begin
|
|
Result:=cIncompatible;
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,1,RaiseOnError) then
|
|
exit;
|
|
Params:=TParamsExpr(Expr);
|
|
|
|
FirstElTypeResolved:=Default(TPasResolverResult);
|
|
for i:=0 to length(Params.Params)-1 do
|
|
begin
|
|
// all params: array
|
|
Param:=Params.Params[i];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
if not (rrfReadable in ParamResolved.Flags)
|
|
or (ParamResolved.BaseType<>btContext)
|
|
or not IsDynArray(ParamResolved.TypeEl) then
|
|
exit(CheckRaiseTypeArgNo(20170329181206,i+1,Param,ParamResolved,'dynamic array',RaiseOnError));
|
|
ComputeElement(TPasArrayType(ParamResolved.TypeEl).ElType,ElTypeResolved,[rcType]);
|
|
Include(ElTypeResolved.Flags,rrfReadable);
|
|
if i=0 then
|
|
begin
|
|
FirstElTypeResolved:=ElTypeResolved;
|
|
Include(ElTypeResolved.Flags,rrfWritable);
|
|
end
|
|
else if CheckAssignResCompatibility(FirstElTypeResolved,ElTypeResolved,Param,RaiseOnError)=cIncompatible then
|
|
exit(cIncompatible);
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.BI_ConcatArray_OnGetCallResult(
|
|
Proc: TResElDataBuiltInProc; Params: TParamsExpr; out
|
|
ResolvedEl: TPasResolverResult);
|
|
begin
|
|
ComputeElement(Params.Params[0],ResolvedEl,[]);
|
|
ResolvedEl.Flags:=ResolvedEl.Flags-[rrfWritable];
|
|
end;
|
|
|
|
function TPasResolver.BI_CopyArray_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
ParamResolved: TPasResolverResult;
|
|
begin
|
|
Result:=cIncompatible;
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,1,RaiseOnError) then
|
|
exit;
|
|
Params:=TParamsExpr(Expr);
|
|
|
|
// first param: array
|
|
Param:=Params.Params[0];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
if (rrfReadable in ParamResolved.Flags)
|
|
and (ParamResolved.BaseType=btContext)
|
|
and IsDynArray(ParamResolved.TypeEl) then
|
|
Result:=cExact;
|
|
if Result=cIncompatible then
|
|
exit(CheckRaiseTypeArgNo(20170329153951,1,Param,ParamResolved,'dynamic array',RaiseOnError));
|
|
if length(Params.Params)=1 then
|
|
exit(cExact);
|
|
|
|
// check optional Start index
|
|
Param:=Params.Params[1];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
if not (rrfReadable in ParamResolved.Flags)
|
|
or not (ParamResolved.BaseType in btAllInteger) then
|
|
exit(CheckRaiseTypeArgNo(20170329164210,2,Param,ParamResolved,'integer',RaiseOnError));
|
|
if length(Params.Params)=2 then
|
|
exit(cExact);
|
|
|
|
// check optional Count
|
|
Param:=Params.Params[2];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
if not (rrfReadable in ParamResolved.Flags)
|
|
or not (ParamResolved.BaseType in btAllInteger) then
|
|
exit(CheckRaiseTypeArgNo(20170329164329,3,Param,ParamResolved,'integer',RaiseOnError));
|
|
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,3,RaiseOnError);
|
|
end;
|
|
|
|
procedure TPasResolver.BI_CopyArray_OnGetCallResult(
|
|
Proc: TResElDataBuiltInProc; Params: TParamsExpr; out
|
|
ResolvedEl: TPasResolverResult);
|
|
begin
|
|
ComputeElement(Params.Params[0],ResolvedEl,[]);
|
|
ResolvedEl.Flags:=ResolvedEl.Flags-[rrfWritable];
|
|
end;
|
|
|
|
function TPasResolver.BI_InsertArray_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
// Insert(Item,var Array,Index)
|
|
var
|
|
Params: TParamsExpr;
|
|
Param, ItemParam: TPasExpr;
|
|
ItemResolved, ParamResolved, ElTypeResolved: TPasResolverResult;
|
|
begin
|
|
Result:=cIncompatible;
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,3,RaiseOnError) then
|
|
exit;
|
|
Params:=TParamsExpr(Expr);
|
|
|
|
// check Item
|
|
ItemParam:=Params.Params[0];
|
|
ComputeElement(ItemParam,ItemResolved,[]);
|
|
if not (rrfReadable in ItemResolved.Flags) then
|
|
exit(CheckRaiseTypeArgNo(20170329171400,1,ItemParam,ItemResolved,'value',RaiseOnError));
|
|
|
|
// check Array
|
|
Param:=Params.Params[1];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
if not ResolvedElCanBeVarParam(ParamResolved) then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170329171514,nVariableIdentifierExpected,sVariableIdentifierExpected,[],Param);
|
|
exit;
|
|
end;
|
|
if (ParamResolved.BaseType<>btContext)
|
|
or not IsDynArray(ParamResolved.TypeEl) then
|
|
exit(CheckRaiseTypeArgNo(20170329172024,2,Param,ParamResolved,'dynamic array',RaiseOnError));
|
|
ComputeElement(TPasArrayType(ParamResolved.TypeEl).ElType,ElTypeResolved,[rcType]);
|
|
if CheckAssignResCompatibility(ElTypeResolved,ItemResolved,ItemParam,RaiseOnError)=cIncompatible then
|
|
exit(cIncompatible);
|
|
|
|
// check insert Index
|
|
Param:=Params.Params[2];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
if not (rrfReadable in ParamResolved.Flags)
|
|
or not (ParamResolved.BaseType in btAllInteger) then
|
|
exit(CheckRaiseTypeArgNo(20170329172348,3,Param,ParamResolved,'integer',RaiseOnError));
|
|
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,3,RaiseOnError);
|
|
end;
|
|
|
|
procedure TPasResolver.BI_InsertArray_OnFinishParamsExpr(
|
|
Proc: TResElDataBuiltInProc; Params: TParamsExpr);
|
|
var
|
|
P: TPasExprArray;
|
|
begin
|
|
if Proc=nil then ;
|
|
P:=Params.Params;
|
|
AccessExpr(P[0],rraRead);
|
|
AccessExpr(P[1],rraVarParam);
|
|
AccessExpr(P[2],rraRead);
|
|
end;
|
|
|
|
function TPasResolver.BI_DeleteArray_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
// Delete(var Array; Start, Count: integer)
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
ParamResolved: TPasResolverResult;
|
|
begin
|
|
Result:=cIncompatible;
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,3,RaiseOnError) then
|
|
exit;
|
|
Params:=TParamsExpr(Expr);
|
|
|
|
// check Array
|
|
Param:=Params.Params[0];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
if not ResolvedElCanBeVarParam(ParamResolved) then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170329173421,nVariableIdentifierExpected,sVariableIdentifierExpected,[],Param);
|
|
exit;
|
|
end;
|
|
if (ParamResolved.BaseType<>btContext)
|
|
or not IsDynArray(ParamResolved.TypeEl) then
|
|
exit(CheckRaiseTypeArgNo(20170329173434,1,Param,ParamResolved,'dynamic array',RaiseOnError));
|
|
|
|
// check param Start
|
|
Param:=Params.Params[1];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
if not (rrfReadable in ParamResolved.Flags)
|
|
or not (ParamResolved.BaseType in btAllInteger) then
|
|
exit(CheckRaiseTypeArgNo(20170329173613,2,Param,ParamResolved,'integer',RaiseOnError));
|
|
|
|
// check param Count
|
|
Param:=Params.Params[2];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
if not (rrfReadable in ParamResolved.Flags)
|
|
or not (ParamResolved.BaseType in btAllInteger) then
|
|
exit(CheckRaiseTypeArgNo(20170329172348,3,Param,ParamResolved,'integer',RaiseOnError));
|
|
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,3,RaiseOnError);
|
|
end;
|
|
|
|
procedure TPasResolver.BI_DeleteArray_OnFinishParamsExpr(
|
|
Proc: TResElDataBuiltInProc; Params: TParamsExpr);
|
|
var
|
|
P: TPasExprArray;
|
|
begin
|
|
if Proc=nil then ;
|
|
P:=Params.Params;
|
|
AccessExpr(P[0],rraVarParam);
|
|
AccessExpr(P[1],rraRead);
|
|
AccessExpr(P[2],rraRead);
|
|
end;
|
|
|
|
function TPasResolver.BI_TypeInfo_OnGetCallCompatibility(
|
|
Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
|
|
var
|
|
Params: TParamsExpr;
|
|
Param: TPasExpr;
|
|
Decl: TPasElement;
|
|
ParamResolved: TPasResolverResult;
|
|
aType: TPasType;
|
|
begin
|
|
Result:=cIncompatible;
|
|
if not CheckBuiltInMinParamCount(Proc,Expr,1,RaiseOnError) then
|
|
exit;
|
|
Params:=TParamsExpr(Expr);
|
|
|
|
// check type or var
|
|
Param:=Params.Params[0];
|
|
ComputeElement(Param,ParamResolved,[rcNoImplicitProc]);
|
|
Decl:=ParamResolved.IdentEl;
|
|
aType:=nil;
|
|
if (Decl<>nil) then
|
|
begin
|
|
if Decl is TPasType then
|
|
aType:=TPasType(Decl)
|
|
else if Decl is TPasVariable then
|
|
aType:=TPasVariable(Decl).VarType
|
|
else if Decl is TPasArgument then
|
|
aType:=TPasArgument(Decl).ArgType;
|
|
end;
|
|
if aType=nil then
|
|
RaiseMsg(20170411100259,nTypeIdentifierExpected,sTypeIdentifierExpected,[],Param);
|
|
aType:=ResolveAliasType(aType);
|
|
if not HasTypeInfo(aType) then
|
|
RaiseMsg(20170413200118,nSymbolCannotBePublished,sSymbolCannotBePublished,[],Param);
|
|
|
|
Result:=CheckBuiltInMaxParamCount(Proc,Params,1,RaiseOnError);
|
|
end;
|
|
|
|
procedure TPasResolver.BI_TypeInfo_OnGetCallResult(Proc: TResElDataBuiltInProc;
|
|
Params: TParamsExpr; out ResolvedEl: TPasResolverResult);
|
|
begin
|
|
if Proc=nil then;
|
|
if Params=nil then ;
|
|
SetResolverTypeExpr(ResolvedEl,btPointer,FBaseTypes[btPointer],[rrfReadable]);
|
|
end;
|
|
|
|
constructor TPasResolver.Create;
|
|
begin
|
|
inherited Create;
|
|
FDefaultScope:=TPasDefaultScope.Create;
|
|
FPendingForwards:=TFPList.Create;
|
|
FBaseTypeStringIndex:=btChar;
|
|
FScopeClass_Class:=TPasClassScope;
|
|
FScopeClass_WithExpr:=TPasWithExprScope;
|
|
PushScope(FDefaultScope);
|
|
end;
|
|
|
|
function TPasResolver.CreateElement(AClass: TPTreeElement; const AName: String;
|
|
AParent: TPasElement; AVisibility: TPasMemberVisibility;
|
|
const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
|
|
var
|
|
aScanner: TPascalScanner;
|
|
SrcPos: TPasSourcePos;
|
|
begin
|
|
// get source position for good error messages
|
|
aScanner:=CurrentParser.Scanner;
|
|
if (ASourceFilename='') or StoreSrcColumns then
|
|
begin
|
|
SrcPos.FileName:=aScanner.CurFilename;
|
|
SrcPos.Row:=aScanner.CurRow;
|
|
SrcPos.Column:=aScanner.CurColumn;
|
|
end
|
|
else
|
|
begin
|
|
SrcPos.FileName:=ASourceFilename;
|
|
SrcPos.Row:=ASourceLinenumber;
|
|
SrcPos.Column:=0;
|
|
end;
|
|
Result:=CreateElement(AClass,AName,AParent,AVisibility,SrcPos);
|
|
end;
|
|
|
|
function TPasResolver.CreateElement(AClass: TPTreeElement; const AName: String;
|
|
AParent: TPasElement; AVisibility: TPasMemberVisibility;
|
|
const ASrcPos: TPasSourcePos): TPasElement;
|
|
var
|
|
El: TPasElement;
|
|
SrcY: integer;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CreateElement ',AClass.ClassName,' Name=',AName,' Parent=',GetObjName(AParent),' (',ASrcPos.Row,',',ASrcPos.Column,')');
|
|
{$ENDIF}
|
|
if (AParent=nil) and (FRootElement<>nil) then
|
|
RaiseInternalError(20160922163535,'more than one root element Class="'+AClass.ClassName+'" Root='+GetObjName(FRootElement));
|
|
|
|
if ASrcPos.FileName='' then
|
|
RaiseInternalError(20160922163541,'missing filename');
|
|
SrcY:=ASrcPos.Row;
|
|
if StoreSrcColumns then
|
|
begin
|
|
if (ASrcPos.Column<ParserMaxEmbeddedColumn)
|
|
and (SrcY<ParserMaxEmbeddedRow) then
|
|
SrcY:=-(SrcY*ParserMaxEmbeddedColumn+integer(ASrcPos.Column));
|
|
end;
|
|
|
|
// create element
|
|
El:=AClass.Create(AName,AParent);
|
|
FLastElement:=El;
|
|
Result:=FLastElement;
|
|
El.Visibility:=AVisibility;
|
|
El.SourceFilename:=ASrcPos.FileName;
|
|
El.SourceLinenumber:=SrcY;
|
|
if FRootElement=nil then
|
|
FRootElement:=Result;
|
|
|
|
// create scope
|
|
if (AClass=TPasVariable)
|
|
or (AClass=TPasConst) then
|
|
AddVariable(TPasVariable(El))
|
|
else if (AClass=TPasProperty) then
|
|
AddProperty(TPasProperty(El))
|
|
else if AClass=TPasArgument then
|
|
AddArgument(TPasArgument(El))
|
|
else if AClass=TPasEnumType then
|
|
AddEnumType(TPasEnumType(El))
|
|
else if AClass=TPasEnumValue then
|
|
AddEnumValue(TPasEnumValue(El))
|
|
else if (AClass=TUnresolvedPendingRef) then
|
|
else if (AClass=TPasAliasType)
|
|
or (AClass=TPasTypeAliasType)
|
|
or (AClass=TPasClassOfType)
|
|
or (AClass=TPasArrayType)
|
|
or (AClass=TPasProcedureType)
|
|
or (AClass=TPasFunctionType)
|
|
or (AClass=TPasSetType)
|
|
or (AClass=TPasRangeType) then
|
|
AddType(TPasType(El))
|
|
else if AClass=TPasRecordType then
|
|
AddRecordType(TPasRecordType(El))
|
|
else if AClass=TPasClassType then
|
|
AddClassType(TPasClassType(El))
|
|
else if AClass=TPasVariant then
|
|
else if AClass.InheritsFrom(TPasProcedure) then
|
|
AddProcedure(TPasProcedure(El))
|
|
else if AClass=TPasResultElement then
|
|
AddFunctionResult(TPasResultElement(El))
|
|
else if AClass=TProcedureBody then
|
|
AddProcedureBody(TProcedureBody(El))
|
|
else if AClass=TPasImplExceptOn then
|
|
AddExceptOn(TPasImplExceptOn(El))
|
|
else if AClass=TPasImplLabelMark then
|
|
else if AClass=TPasOverloadedProc then
|
|
else if (AClass=TInterfaceSection)
|
|
or (AClass=TImplementationSection)
|
|
or (AClass=TProgramSection)
|
|
or (AClass=TLibrarySection) then
|
|
AddSection(TPasSection(El))
|
|
else if (AClass=TPasModule)
|
|
or (AClass=TPasProgram)
|
|
or (AClass=TPasLibrary) then
|
|
AddModule(TPasModule(El))
|
|
else if AClass.InheritsFrom(TPasExpr) then
|
|
// resolved when finished
|
|
else if AClass.InheritsFrom(TPasImplBlock) then
|
|
// resolved finished
|
|
else
|
|
RaiseNotYetImplemented(20160922163544,El);
|
|
end;
|
|
|
|
function TPasResolver.FindElement(const aName: String): TPasElement;
|
|
// called by TPasParser
|
|
var
|
|
p: SizeInt;
|
|
RightPath, CurName: String;
|
|
NeedPop: Boolean;
|
|
CurScopeEl, NextEl, ErrorEl: TPasElement;
|
|
begin
|
|
//writeln('TPasResolver.FindElement Name="',aName,'"');
|
|
ErrorEl:=nil; // use nil to use scanner position as error position
|
|
|
|
RightPath:=aName;
|
|
p:=1;
|
|
CurScopeEl:=nil;
|
|
repeat
|
|
p:=Pos('.',RightPath);
|
|
if p<1 then
|
|
begin
|
|
CurName:=RightPath;
|
|
RightPath:='';
|
|
end
|
|
else
|
|
begin
|
|
CurName:=LeftStr(RightPath,p-1);
|
|
Delete(RightPath,1,p);
|
|
if RightPath='' then
|
|
RaiseMsg(20170328003146,nIllegalExpression,sIllegalExpression,[],ErrorEl);
|
|
end;
|
|
{$IFDEF VerbosePasResolver}
|
|
if RightPath<>'' then
|
|
writeln('TPasResolver.FindElement searching scope "',CurName,'" RightPath="',RightPath,'" ...');
|
|
{$ENDIF}
|
|
if not IsValidIdent(CurName) then
|
|
RaiseNotYetImplemented(20170328000033,ErrorEl);
|
|
|
|
if CurScopeEl<>nil then
|
|
begin
|
|
NeedPop:=true;
|
|
if CurScopeEl.ClassType=TPasClassType then
|
|
// check visibility
|
|
PushClassDotScope(TPasClassType(CurScopeEl))
|
|
else if CurScopeEl is TPasModule then
|
|
PushModuleDotScope(TPasModule(CurScopeEl));
|
|
end
|
|
else
|
|
NeedPop:=false;
|
|
|
|
NextEl:=FindElementWithoutParams(CurName,ErrorEl,true);
|
|
if RightPath<>'' then
|
|
begin
|
|
if (NextEl is TPasModule) then
|
|
begin
|
|
if CurScopeEl is TPasModule then
|
|
RaiseXExpectedButYFound(20170328001619,'class',NextEl.ElementTypeName+' '+NextEl.Name,ErrorEl);
|
|
CurScopeEl:=NextEl;
|
|
end
|
|
else if (CurScopeEl is TPasClassType) then
|
|
CurScopeEl:=NextEl
|
|
else
|
|
RaiseIdentifierNotFound(20170328001941,CurName,ErrorEl);
|
|
end;
|
|
|
|
// restore scope
|
|
if NeedPop then
|
|
PopScope;
|
|
|
|
if RightPath='' then
|
|
exit(NextEl);
|
|
until false;
|
|
end;
|
|
|
|
function TPasResolver.FindElementWithoutParams(const AName: String;
|
|
ErrorPosEl: TPasElement; NoProcsWithArgs: boolean): TPasElement;
|
|
var
|
|
Data: TPRFindData;
|
|
begin
|
|
Result:=FindElementWithoutParams(AName,Data,ErrorPosEl,NoProcsWithArgs);
|
|
if Data.Found=nil then exit; // forward type: class-of or ^
|
|
CheckFoundElement(Data,nil);
|
|
if (Data.StartScope<>nil) and (Data.StartScope.ClassType=ScopeClass_WithExpr)
|
|
and (wesfNeedTmpVar in TPasWithExprScope(Data.StartScope).Flags) then
|
|
RaiseInternalError(20160923111727); // caller forgot to handle "With", use the other FindElementWithoutParams instead
|
|
end;
|
|
|
|
function TPasResolver.FindElementWithoutParams(const AName: String; out
|
|
Data: TPRFindData; ErrorPosEl: TPasElement; NoProcsWithArgs: boolean
|
|
): TPasElement;
|
|
var
|
|
Abort: boolean;
|
|
begin
|
|
//writeln('TPasResolver.FindIdentifier Name="',AName,'"');
|
|
Result:=Nil;
|
|
Abort:=false;
|
|
Data:=Default(TPRFindData);
|
|
Data.ErrorPosEl:=ErrorPosEl;
|
|
IterateElements(AName,@OnFindFirstElement,@Data,Abort);
|
|
Result:=Data.Found;
|
|
if Result=nil then
|
|
begin
|
|
if (ErrorPosEl=nil) and (LastElement<>nil)
|
|
and (LastElement.ClassType=TPasClassOfType)
|
|
and (TPasClassOfType(LastElement).DestType=nil) then
|
|
begin
|
|
// 'class of' of a not yet defined class
|
|
Result:=CreateElement(TUnresolvedPendingRef,AName,LastElement,visDefault,
|
|
CurrentParser.Scanner.CurSourcePos);
|
|
exit;
|
|
end;
|
|
RaiseIdentifierNotFound(20170216152722,AName,ErrorPosEl);
|
|
end;
|
|
if NoProcsWithArgs and (Result is TPasProcedure)
|
|
and ProcNeedsParams(TPasProcedure(Result).ProcType)
|
|
then
|
|
// proc needs parameters
|
|
RaiseMsg(20170216152347,nWrongNumberOfParametersForCallTo,
|
|
sWrongNumberOfParametersForCallTo,[GetProcDesc(TPasProcedure(Result).ProcType)],ErrorPosEl);
|
|
end;
|
|
|
|
procedure TPasResolver.IterateElements(const aName: string;
|
|
const OnIterateElement: TIterateScopeElement; Data: Pointer;
|
|
var Abort: boolean);
|
|
var
|
|
i: Integer;
|
|
Scope: TPasScope;
|
|
begin
|
|
for i:=FScopeCount-1 downto 0 do
|
|
begin
|
|
Scope:=Scopes[i];
|
|
Scope.IterateElements(AName,Scope,OnIterateElement,Data,Abort);
|
|
if Abort then
|
|
exit;
|
|
if Scope is TPasSubScope then break;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.CheckFoundElement(
|
|
const FindData: TPRFindData; Ref: TResolvedReference);
|
|
// check visibility rules
|
|
// Call this method after finding an element by searching the scopes.
|
|
var
|
|
Proc: TPasProcedure;
|
|
Context: TPasElement;
|
|
FoundContext: TPasClassType;
|
|
StartScope: TPasScope;
|
|
OnlyTypeMembers: Boolean;
|
|
TypeEl: TPasType;
|
|
C: TClass;
|
|
begin
|
|
StartScope:=FindData.StartScope;
|
|
OnlyTypeMembers:=false;
|
|
if StartScope is TPasDotIdentifierScope then
|
|
begin
|
|
OnlyTypeMembers:=TPasDotIdentifierScope(StartScope).OnlyTypeMembers;
|
|
Include(Ref.Flags,rrfDotScope);
|
|
if TPasDotIdentifierScope(StartScope).ConstParent then
|
|
Include(Ref.Flags,rrfConstInherited);
|
|
end
|
|
else if StartScope.ClassType=ScopeClass_WithExpr then
|
|
begin
|
|
OnlyTypeMembers:=wesfOnlyTypeMembers in TPasWithExprScope(StartScope).Flags;
|
|
Include(Ref.Flags,rrfDotScope);
|
|
if wesfConstParent in TPasWithExprScope(StartScope).Flags then
|
|
Include(Ref.Flags,rrfConstInherited);
|
|
end
|
|
else if StartScope.ClassType=TPasProcedureScope then
|
|
begin
|
|
Proc:=TPasProcedureScope(StartScope).Element as TPasProcedure;
|
|
//writeln('TPasResolver.CheckFoundElement ',GetObjName(Proc),' ',IsClassMethod(Proc),' ElScope=',GetObjName(FindData.ElScope));
|
|
if (FindData.ElScope<>StartScope) and IsClassMethod(Proc) then
|
|
OnlyTypeMembers:=true;
|
|
end;
|
|
|
|
//writeln('TPasResolver.CheckFoundElOnStartScope StartScope=',StartScope.ClassName,
|
|
// ' StartIsDot=',StartScope is TPasDotIdentifierScope,
|
|
// ' OnlyTypeMembers=',(StartScope is TPasDotIdentifierScope)
|
|
// and TPasDotIdentifierScope(StartScope).OnlyTypeMembers,
|
|
// ' FindData.Found=',GetObjName(FindData.Found));
|
|
if OnlyTypeMembers then
|
|
begin
|
|
//writeln('TPasResolver.CheckFoundElOnStartScope ',GetObjName(FindData.Found),' ',(FindData.Found is TPasVariable)
|
|
// and (vmClass in TPasVariable(FindData.Found).VarModifiers));
|
|
// only class vars/procs allowed
|
|
if (FindData.Found.ClassType=TPasConstructor) then
|
|
// constructor: ok
|
|
else if IsClassMethod(FindData.Found)
|
|
then
|
|
// class proc: ok
|
|
else if (FindData.Found is TPasVariable)
|
|
and (vmClass in TPasVariable(FindData.Found).VarModifiers) then
|
|
// class var/const/property: ok
|
|
else
|
|
begin
|
|
RaiseMsg(20170216152348,nCannotAccessThisMemberFromAX,
|
|
sCannotAccessThisMemberFromAX,[FindData.Found.Parent.ElementTypeName],FindData.ErrorPosEl);
|
|
end;
|
|
end
|
|
else if (proExtClassInstanceNoTypeMembers in Options)
|
|
and (StartScope.ClassType=TPasDotClassScope)
|
|
and TPasClassType(TPasDotClassScope(StartScope).ClassScope.Element).IsExternal then
|
|
begin
|
|
// found member in external class instance
|
|
C:=FindData.Found.ClassType;
|
|
if (C=TPasProcedure) or (C=TPasFunction) then
|
|
// ok
|
|
else if C.InheritsFrom(TPasVariable)
|
|
and (not (vmClass in TPasVariable(FindData.Found).VarModifiers)) then
|
|
// ok
|
|
else
|
|
begin
|
|
RaiseMsg(20170331184224,nExternalClassInstanceCannotAccessStaticX,
|
|
sExternalClassInstanceCannotAccessStaticX,
|
|
[FindData.Found.ElementTypeName+' '+FindData.Found.Name],
|
|
FindData.ErrorPosEl);
|
|
end;
|
|
end;
|
|
|
|
if (FindData.Found is TPasProcedure) then
|
|
begin
|
|
Proc:=TPasProcedure(FindData.Found);
|
|
if Proc.IsVirtual or Proc.IsOverride then
|
|
begin
|
|
if (StartScope.ClassType=TPasDotClassScope)
|
|
and TPasDotClassScope(StartScope).InheritedExpr then
|
|
begin
|
|
// call directly
|
|
if Proc.IsAbstract then
|
|
RaiseMsg(20170216152352,nAbstractMethodsCannotBeCalledDirectly,
|
|
sAbstractMethodsCannotBeCalledDirectly,[],FindData.ErrorPosEl);
|
|
end
|
|
else
|
|
begin
|
|
// call via virtual method table
|
|
if Ref<>nil then
|
|
Ref.Flags:=Ref.Flags+[rrfVMT];
|
|
end;
|
|
end;
|
|
|
|
// constructor: NewInstance or normal call
|
|
// it is a NewInstance iff the scope is a class, e.g. TObject.Create
|
|
if (Proc.ClassType=TPasConstructor)
|
|
and OnlyTypeMembers
|
|
and (Ref<>nil) then
|
|
begin
|
|
Ref.Flags:=Ref.Flags+[rrfNewInstance]-[rrfConstInherited];
|
|
// store the class in Ref.Context
|
|
if Ref.Context<>nil then
|
|
RaiseInternalError(20170131141936);
|
|
Ref.Context:=TResolvedRefCtxConstructor.Create;
|
|
if StartScope is TPasDotClassScope then
|
|
TypeEl:=TPasDotClassScope(StartScope).ClassScope.Element as TPasType
|
|
else if (StartScope is TPasWithExprScope)
|
|
and (TPasWithExprScope(StartScope).Scope is TPasClassScope) then
|
|
TypeEl:=TPasClassScope(TPasWithExprScope(StartScope).Scope).Element as TPasType
|
|
else if (StartScope is TPasProcedureScope) then
|
|
TypeEl:=TPasProcedureScope(StartScope).ClassScope.Element as TPasType
|
|
else
|
|
RaiseInternalError(20170131150855,GetObjName(StartScope));
|
|
TResolvedRefCtxConstructor(Ref.Context).Typ:=TypeEl;
|
|
end;
|
|
{$IFDEF VerbosePasResolver}
|
|
if (Proc.ClassType=TPasConstructor) then
|
|
begin
|
|
write('TPasResolver.CheckFoundElement ',GetObjName(Proc));
|
|
if Ref=nil then
|
|
write(' no ref!')
|
|
else
|
|
begin
|
|
write(' rrfNewInstance=',rrfNewInstance in Ref.Flags,
|
|
' StartScope=',GetObjName(StartScope),
|
|
' OnlyTypeMembers=',OnlyTypeMembers);
|
|
end;
|
|
writeln;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
// destructor: FreeInstance or normal call
|
|
// it is a normal call if 'inherited'
|
|
if (Proc.ClassType=TPasDestructor) and (Ref<>nil) then
|
|
if ((StartScope.ClassType<>TPasDotClassScope)
|
|
or (not TPasDotClassScope(StartScope).InheritedExpr)) then
|
|
Ref.Flags:=Ref.Flags+[rrfFreeInstance];
|
|
{$IFDEF VerbosePasResolver}
|
|
if (Proc.ClassType=TPasDestructor) then
|
|
begin
|
|
write('TPasResolver.CheckFoundElement ',GetObjName(Proc));
|
|
if Ref=nil then
|
|
write(' no ref!')
|
|
else
|
|
begin
|
|
write(' rrfFreeInstance=',rrfFreeInstance in Ref.Flags,
|
|
' StartScope=',GetObjName(StartScope));
|
|
if StartScope.ClassType=TPasDotClassScope then
|
|
write(' InheritedExpr=',TPasDotClassScope(StartScope).InheritedExpr);
|
|
end;
|
|
writeln;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
// check class visibility
|
|
if FindData.Found.Visibility in [visPrivate,visProtected,visStrictPrivate,visStrictProtected] then
|
|
begin
|
|
Context:=GetVisibilityContext;
|
|
FoundContext:=FindData.Found.Parent as TPasClassType;
|
|
case FindData.Found.Visibility of
|
|
visPrivate:
|
|
// private members can only be accessed in same module
|
|
if FoundContext.GetModule<>Context.GetModule then
|
|
RaiseMsg(20170216152354,nCantAccessPrivateMember,sCantAccessPrivateMember,
|
|
['private',FindData.Found.Name],FindData.ErrorPosEl);
|
|
visProtected:
|
|
// protected members can only be accessed in same module or descendant classes
|
|
if FoundContext.GetModule=Context.GetModule then
|
|
// same module -> ok
|
|
else if (Context is TPasType)
|
|
and (CheckClassIsClass(TPasType(Context),FoundContext,FindData.ErrorPosEl)<>cIncompatible) then
|
|
// context in class or descendant
|
|
else
|
|
RaiseMsg(20170216152356,nCantAccessPrivateMember,sCantAccessPrivateMember,
|
|
['protected',FindData.Found.Name],FindData.ErrorPosEl);
|
|
visStrictPrivate:
|
|
// strict private members can only be accessed in their class
|
|
if Context<>FoundContext then
|
|
RaiseMsg(20170216152357,nCantAccessPrivateMember,sCantAccessPrivateMember,
|
|
['strict private',FindData.Found.Name],FindData.ErrorPosEl);
|
|
visStrictProtected:
|
|
// strict protected members can only be access in their and descendant classes
|
|
if (Context is TPasType)
|
|
and (CheckClassIsClass(TPasType(Context),FoundContext,FindData.ErrorPosEl)<>cIncompatible) then
|
|
// context in class or descendant
|
|
else
|
|
RaiseMsg(20170216152400,nCantAccessPrivateMember,sCantAccessPrivateMember,
|
|
['strict protected',FindData.Found.Name],FindData.ErrorPosEl);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TPasResolver.GetVisibilityContext: TPasElement;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=ScopeCount-1 downto 0 do
|
|
begin
|
|
Result:=Scopes[i].VisibilityContext;
|
|
if Result<>nil then exit;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
procedure TPasResolver.FinishScope(ScopeType: TPasScopeType; El: TPasElement);
|
|
begin
|
|
case ScopeType of
|
|
stModule: FinishModule(El as TPasModule);
|
|
stUsesList: FinishUsesList;
|
|
stTypeSection: FinishTypeSection(El as TPasDeclarations);
|
|
stTypeDef: FinishTypeDef(El as TPasType);
|
|
stConstDef: FinishConstDef(El as TPasConst);
|
|
stProcedure: FinishProcedure(El as TPasProcedure);
|
|
stProcedureHeader: FinishProcedureType(El as TPasProcedureType);
|
|
stExceptOnExpr: FinishExceptOnExpr;
|
|
stExceptOnStatement: FinishExceptOnStatement;
|
|
stDeclaration: FinishDeclaration(El);
|
|
stAncestors: FinishAncestors(El as TPasClassType);
|
|
else
|
|
RaiseMsg(20170216152401,nNotYetImplemented,sNotYetImplemented+' FinishScope',[IntToStr(ord(ScopeType))],nil);
|
|
end;
|
|
end;
|
|
|
|
class procedure TPasResolver.UnmangleSourceLineNumber(LineNumber: integer; out
|
|
Line, Column: integer);
|
|
begin
|
|
Line:=Linenumber;
|
|
Column:=0;
|
|
if Line<0 then begin
|
|
Line:=-Line;
|
|
Column:=Line mod ParserMaxEmbeddedColumn;
|
|
Line:=Line div ParserMaxEmbeddedColumn;
|
|
end;
|
|
end;
|
|
|
|
class function TPasResolver.GetElementSourcePosStr(El: TPasElement): string;
|
|
var
|
|
Line, Column: integer;
|
|
begin
|
|
if El=nil then exit('nil');
|
|
UnmangleSourceLineNumber(El.SourceLinenumber,Line,Column);
|
|
Result:=El.SourceFilename+'('+IntToStr(Line);
|
|
if Column>0 then
|
|
Result:=Result+','+IntToStr(Column);
|
|
Result:=Result+')';
|
|
end;
|
|
|
|
destructor TPasResolver.Destroy;
|
|
begin
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasResolver.Destroy START ',ClassName);
|
|
{$ENDIF}
|
|
Clear;
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasResolver.Destroy PopScope...');
|
|
{$ENDIF}
|
|
PopScope; // free default scope
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasResolver.Destroy FPendingForwards...');
|
|
{$ENDIF}
|
|
FreeAndNil(FPendingForwards);
|
|
inherited Destroy;
|
|
{$IFDEF VerbosePasResolverMem}
|
|
writeln('TPasResolver.Destroy END ',ClassName);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TPasResolver.Clear;
|
|
begin
|
|
RestoreSubScopes(0);
|
|
// clear stack, keep DefaultScope
|
|
while (FScopeCount>0) and (FTopScope<>DefaultScope) do
|
|
PopScope;
|
|
ClearResolveDataList(lkModule);
|
|
end;
|
|
|
|
procedure TPasResolver.ClearBuiltInIdentifiers;
|
|
var
|
|
bt: TResolverBaseType;
|
|
begin
|
|
ClearResolveDataList(lkBuiltIn);
|
|
for bt in TResolverBaseType do
|
|
FBaseTypes[bt]:=nil;
|
|
end;
|
|
|
|
procedure TPasResolver.AddObjFPCBuiltInIdentifiers(
|
|
const TheBaseTypes: TResolveBaseTypes;
|
|
const TheBaseProcs: TResolverBuiltInProcs);
|
|
var
|
|
bt: TResolverBaseType;
|
|
begin
|
|
for bt in TheBaseTypes do
|
|
AddBaseType(BaseTypeNames[bt],bt);
|
|
if bfLength in TheBaseProcs then
|
|
AddBuiltInProc('Length','function Length(const String or Array): sizeint',
|
|
@BI_Length_OnGetCallCompatibility,@BI_Length_OnGetCallResult,nil,bfLength);
|
|
if bfSetLength in TheBaseProcs then
|
|
AddBuiltInProc('SetLength','procedure SetLength(var String or Array; NewLength: sizeint)',
|
|
@BI_SetLength_OnGetCallCompatibility,nil,
|
|
@BI_SetLength_OnFinishParamsExpr,bfSetLength,[bipfCanBeStatement]);
|
|
if bfInclude in TheBaseProcs then
|
|
AddBuiltInProc('Include','procedure Include(var Set of Enum; const Enum)',
|
|
@BI_InExclude_OnGetCallCompatibility,nil,
|
|
@BI_InExclude_OnFinishParamsExpr,bfInclude,[bipfCanBeStatement]);
|
|
if bfExclude in TheBaseProcs then
|
|
AddBuiltInProc('Exclude','procedure Exclude(var Set of Enum; const Enum)',
|
|
@BI_InExclude_OnGetCallCompatibility,nil,
|
|
@BI_InExclude_OnFinishParamsExpr,bfExclude,[bipfCanBeStatement]);
|
|
if bfBreak in TheBaseProcs then
|
|
AddBuiltInProc('Break','procedure Break',
|
|
@BI_Break_OnGetCallCompatibility,nil,nil,bfBreak,[bipfCanBeStatement]);
|
|
if bfContinue in TheBaseProcs then
|
|
AddBuiltInProc('Continue','procedure Continue',
|
|
@BI_Continue_OnGetCallCompatibility,nil,nil,bfContinue,[bipfCanBeStatement]);
|
|
if bfExit in TheBaseProcs then
|
|
AddBuiltInProc('Exit','procedure Exit(result)',
|
|
@BI_Exit_OnGetCallCompatibility,nil,nil,bfExit,[bipfCanBeStatement]);
|
|
if bfInc in TheBaseProcs then
|
|
AddBuiltInProc('Inc','procedure Inc(var Integer; const Incr: Integer = 1)',
|
|
@BI_IncDec_OnGetCallCompatibility,nil,
|
|
@BI_IncDec_OnFinishParamsExpr,bfInc,[bipfCanBeStatement]);
|
|
if bfDec in TheBaseProcs then
|
|
AddBuiltInProc('Dec','procedure Dec(var Integer; const Decr: Integer = 1)',
|
|
@BI_IncDec_OnGetCallCompatibility,nil,
|
|
@BI_IncDec_OnFinishParamsExpr,bfDec,[bipfCanBeStatement]);
|
|
if bfAssigned in TheBaseProcs then
|
|
AddBuiltInProc('Assigned','function Assigned(const Pointer or Class or Class-of): boolean',
|
|
@BI_Assigned_OnGetCallCompatibility,@BI_Assigned_OnGetCallResult,nil,bfAssigned);
|
|
if bfChr in TheBaseProcs then
|
|
AddBuiltInProc('Chr','function Chr(const Integer): char',
|
|
@BI_Chr_OnGetCallCompatibility,@BI_Chr_OnGetCallResult,nil,bfChr);
|
|
if bfOrd in TheBaseProcs then
|
|
AddBuiltInProc('Ord','function Ord(const Enum or Char): integer',
|
|
@BI_Ord_OnGetCallCompatibility,@BI_Ord_OnGetCallResult,nil,bfOrd);
|
|
if bfLow in TheBaseProcs then
|
|
AddBuiltInProc('Low','function Low(const array or ordinal): ordinal or integer',
|
|
@BI_LowHigh_OnGetCallCompatibility,@BI_LowHigh_OnGetCallResult,nil,bfLow);
|
|
if bfHigh in TheBaseProcs then
|
|
AddBuiltInProc('High','function High(const array or ordinal): ordinal or integer',
|
|
@BI_LowHigh_OnGetCallCompatibility,@BI_LowHigh_OnGetCallResult,nil,bfHigh);
|
|
if bfPred in TheBaseProcs then
|
|
AddBuiltInProc('Pred','function Pred(const ordinal): ordinal',
|
|
@BI_PredSucc_OnGetCallCompatibility,@BI_PredSucc_OnGetCallResult,nil,bfPred);
|
|
if bfSucc in TheBaseProcs then
|
|
AddBuiltInProc('Succ','function Succ(const ordinal): ordinal',
|
|
@BI_PredSucc_OnGetCallCompatibility,@BI_PredSucc_OnGetCallResult,nil,bfSucc);
|
|
if bfStrProc in TheBaseProcs then
|
|
AddBuiltInProc('Str','procedure Str(const var; var String)',
|
|
@BI_StrProc_OnGetCallCompatibility,nil,
|
|
@BI_StrProc_OnFinishParamsExpr,bfStrProc,[bipfCanBeStatement]);
|
|
if bfStrFunc in TheBaseProcs then
|
|
AddBuiltInProc('Str','function Str(const var): String',
|
|
@BI_StrFunc_OnGetCallCompatibility,@BI_StrFunc_OnGetCallResult,nil,bfStrFunc);
|
|
if bfConcatArray in TheBaseProcs then
|
|
AddBuiltInProc('Concat','function Concat(const Array1, Array2, ...): Array',
|
|
@BI_ConcatArray_OnGetCallCompatibility,@BI_ConcatArray_OnGetCallResult,nil,bfConcatArray);
|
|
if bfCopyArray in TheBaseProcs then
|
|
AddBuiltInProc('Copy','function Copy(const Array; Start: integer = 0; Count: integer = all): Array',
|
|
@BI_CopyArray_OnGetCallCompatibility,@BI_CopyArray_OnGetCallResult,nil,bfCopyArray);
|
|
if bfInsertArray in TheBaseProcs then
|
|
AddBuiltInProc('Insert','procedure Insert(const Element; var Array; Index: integer)',
|
|
@BI_InsertArray_OnGetCallCompatibility,nil,
|
|
@BI_InsertArray_OnFinishParamsExpr,bfInsertArray,[bipfCanBeStatement]);
|
|
if bfDeleteArray in TheBaseProcs then
|
|
AddBuiltInProc('Delete','procedure Delete(var Array; Start, Count: integer)',
|
|
@BI_DeleteArray_OnGetCallCompatibility,nil,
|
|
@BI_DeleteArray_OnFinishParamsExpr,bfDeleteArray,[bipfCanBeStatement]);
|
|
if bfTypeInfo in TheBaseProcs then
|
|
AddBuiltInProc('TypeInfo','function TypeInfo(type or var identifier): Pointer',
|
|
@BI_TypeInfo_OnGetCallCompatibility,@BI_TypeInfo_OnGetCallResult,
|
|
nil,bfTypeInfo);
|
|
end;
|
|
|
|
function TPasResolver.AddBaseType(const aName: string; Typ: TResolverBaseType
|
|
): TResElDataBaseType;
|
|
var
|
|
El: TPasUnresolvedSymbolRef;
|
|
begin
|
|
El:=TPasUnresolvedSymbolRef.Create(aName,nil);
|
|
if not (Typ in [btNone,btCustom]) then
|
|
FBaseTypes[Typ]:=El;
|
|
Result:=TResElDataBaseType.Create;
|
|
Result.BaseType:=Typ;
|
|
AddResolveData(El,Result,lkBuiltIn);
|
|
FDefaultScope.AddIdentifier(aName,El,pikBaseType);
|
|
end;
|
|
|
|
function TPasResolver.AddCustomBaseType(const aName: string;
|
|
aClass: TResElDataBaseTypeClass): TPasUnresolvedSymbolRef;
|
|
var
|
|
CustomData: TResElDataBaseType;
|
|
begin
|
|
Result:=TPasUnresolvedSymbolRef.Create(aName,nil);
|
|
CustomData:=aClass.Create;
|
|
CustomData.BaseType:=btCustom;
|
|
AddResolveData(Result,CustomData,lkBuiltIn);
|
|
FDefaultScope.AddIdentifier(aName,Result,pikBaseType);
|
|
end;
|
|
|
|
function TPasResolver.IsBaseType(aType: TPasType; BaseType: TResolverBaseType
|
|
): boolean;
|
|
begin
|
|
Result:=false;
|
|
if aType=nil then exit;
|
|
if aType.ClassType<>TPasUnresolvedSymbolRef then exit;
|
|
Result:=CompareText(aType.Name,BaseTypeNames[BaseType])=0;
|
|
end;
|
|
|
|
function TPasResolver.AddBuiltInProc(const aName: string; Signature: string;
|
|
const GetCallCompatibility: TOnGetCallCompatibility;
|
|
const GetCallResult: TOnGetCallResult;
|
|
const FinishParamsExpr: TOnFinishParamsExpr;
|
|
const BuiltIn: TResolverBuiltInProc; const Flags: TBuiltInProcFlags
|
|
): TResElDataBuiltInProc;
|
|
var
|
|
El: TPasUnresolvedSymbolRef;
|
|
begin
|
|
El:=TPasUnresolvedSymbolRef.Create(aName,nil);
|
|
Result:=TResElDataBuiltInProc.Create;
|
|
Result.Proc:=El;
|
|
Result.Signature:=Signature;
|
|
Result.BuiltIn:=BuiltIn;
|
|
Result.GetCallCompatibility:=GetCallCompatibility;
|
|
Result.GetCallResult:=GetCallResult;
|
|
Result.FinishParamsExpression:=FinishParamsExpr;
|
|
Result.Flags:=Flags;
|
|
AddResolveData(El,Result,lkBuiltIn);
|
|
FDefaultScope.AddIdentifier(aName,El,pikBuiltInProc);
|
|
end;
|
|
|
|
procedure TPasResolver.AddResolveData(El: TPasElement; Data: TResolveData;
|
|
Kind: TResolveDataListKind);
|
|
begin
|
|
Data.Element:=El;
|
|
Data.Owner:=Self;
|
|
Data.Next:=FLastCreatedData[Kind];
|
|
FLastCreatedData[Kind]:=Data;
|
|
El.CustomData:=Data;
|
|
end;
|
|
|
|
function TPasResolver.CreateReference(DeclEl, RefEl: TPasElement;
|
|
Access: TResolvedRefAccess; FindData: PPRFindData): TResolvedReference;
|
|
|
|
procedure RaiseAlreadySet;
|
|
var
|
|
FormerDeclEl: TPasElement;
|
|
begin
|
|
writeln('RaiseAlreadySet RefEl=',GetObjName(RefEl),' DeclEl=',GetObjName(DeclEl));
|
|
writeln(' RefEl at ',GetElementSourcePosStr(RefEl));
|
|
writeln(' RefEl.CustomData=',GetObjName(RefEl.CustomData));
|
|
if RefEl.CustomData is TResolvedReference then
|
|
begin
|
|
FormerDeclEl:=TResolvedReference(RefEl.CustomData).Declaration;
|
|
writeln(' TResolvedReference(RefEl.CustomData).Declaration=',GetObjName(FormerDeclEl),
|
|
' IsSame=',FormerDeclEl=DeclEl);
|
|
end;
|
|
RaiseInternalError(20160922163554,'customdata<>nil');
|
|
end;
|
|
|
|
begin
|
|
if RefEl.CustomData<>nil then
|
|
RaiseAlreadySet;
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CreateReference RefEl=',GetObjName(RefEl),' DeclEl=',GetObjName(DeclEl));
|
|
{$ENDIF}
|
|
Result:=TResolvedReference.Create;
|
|
if FindData<>nil then
|
|
begin
|
|
if FindData^.StartScope.ClassType=ScopeClass_WithExpr then
|
|
Result.WithExprScope:=TPasWithExprScope(FindData^.StartScope);
|
|
end;
|
|
AddResolveData(RefEl,Result,lkModule);
|
|
Result.Declaration:=DeclEl;
|
|
if RefEl is TPasExpr then
|
|
SetResolvedRefAccess(TPasExpr(RefEl),Result,Access);
|
|
end;
|
|
|
|
function TPasResolver.CreateScope(El: TPasElement; ScopeClass: TPasScopeClass
|
|
): TPasScope;
|
|
begin
|
|
if not ScopeClass.IsStoredInElement then
|
|
RaiseInternalError(20160923121858);
|
|
if El.CustomData<>nil then
|
|
RaiseInternalError(20160923121849);
|
|
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CreateScope El=',GetObjName(El),' ScopeClass=',ScopeClass.ClassName);
|
|
{$ENDIF}
|
|
Result:=ScopeClass.Create;
|
|
if Result.FreeOnPop then
|
|
begin
|
|
Result.Element:=El;
|
|
El.CustomData:=Result;
|
|
Result.Owner:=Self;
|
|
end
|
|
else
|
|
// add to free list
|
|
AddResolveData(El,Result,lkModule);
|
|
end;
|
|
|
|
procedure TPasResolver.PopScope;
|
|
var
|
|
Scope: TPasScope;
|
|
begin
|
|
if FScopeCount=0 then
|
|
RaiseInternalError(20160922163557);
|
|
{$IFDEF VerbosePasResolver}
|
|
//writeln('TPasResolver.PopScope ',FScopeCount,' ',FTopScope<>nil,' IsDefault=',FTopScope=FDefaultScope);
|
|
writeln('TPasResolver.PopScope ',FTopScope.ClassName,' IsStoredInElement=',FTopScope.IsStoredInElement,' Element=',GetObjName(FTopScope.Element),' FreeOnPop=',FTopScope.FreeOnPop);
|
|
{$ENDIF}
|
|
dec(FScopeCount);
|
|
if FTopScope.FreeOnPop then
|
|
begin
|
|
Scope:=FScopes[FScopeCount];
|
|
if (Scope.Element<>nil) and (Scope.Element.CustomData=Scope) then
|
|
Scope.Element.CustomData:=nil;
|
|
if Scope=FDefaultScope then
|
|
FDefaultScope:=nil;
|
|
FScopes[FScopeCount]:=nil;
|
|
Scope.Free;
|
|
end;
|
|
if FScopeCount>0 then
|
|
FTopScope:=FScopes[FScopeCount-1]
|
|
else
|
|
FTopScope:=nil;
|
|
end;
|
|
|
|
procedure TPasResolver.PushScope(Scope: TPasScope);
|
|
begin
|
|
if Scope=nil then
|
|
RaiseInternalError(20160922163601);
|
|
if length(FScopes)=FScopeCount then
|
|
SetLength(FScopes,FScopeCount*2+10);
|
|
FScopes[FScopeCount]:=Scope;
|
|
inc(FScopeCount);
|
|
FTopScope:=Scope;
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.PushScope ScopeCount=',ScopeCount,' ',GetObjName(FTopScope));
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TPasResolver.PushScope(El: TPasElement; ScopeClass: TPasScopeClass
|
|
): TPasScope;
|
|
begin
|
|
Result:=CreateScope(El,ScopeClass);
|
|
PushScope(Result);
|
|
end;
|
|
|
|
function TPasResolver.PushModuleDotScope(aModule: TPasModule): TPasModuleDotScope;
|
|
begin
|
|
Result:=TPasModuleDotScope.Create;
|
|
Result.Owner:=Self;
|
|
Result.Module:=aModule;
|
|
if aModule is TPasProgram then
|
|
begin // program
|
|
if TPasProgram(aModule).ProgramSection<>nil then
|
|
Result.InterfaceScope:=
|
|
TPasProgram(aModule).ProgramSection.CustomData as TPasSectionScope;
|
|
end
|
|
else if aModule is TPasLibrary then
|
|
begin // library
|
|
if TPasLibrary(aModule).LibrarySection<>nil then
|
|
Result.InterfaceScope:=
|
|
TPasLibrary(aModule).LibrarySection.CustomData as TPasSectionScope;
|
|
end
|
|
else
|
|
begin // unit
|
|
if aModule.InterfaceSection<>nil then
|
|
Result.InterfaceScope:=
|
|
aModule.InterfaceSection.CustomData as TPasSectionScope;
|
|
if (aModule=CurrentParser.CurModule)
|
|
and (aModule.ImplementationSection<>nil)
|
|
and (aModule.ImplementationSection.CustomData<>nil)
|
|
then
|
|
Result.ImplementationScope:=aModule.ImplementationSection.CustomData as TPasSectionScope;
|
|
end;
|
|
|
|
PushScope(Result);
|
|
end;
|
|
|
|
function TPasResolver.PushClassDotScope(var CurClassType: TPasClassType
|
|
): TPasDotClassScope;
|
|
var
|
|
ClassScope: TPasClassScope;
|
|
Ref: TResolvedReference;
|
|
begin
|
|
if CurClassType.IsForward then
|
|
begin
|
|
Ref:=CurClassType.CustomData as TResolvedReference;
|
|
CurClassType:=Ref.Declaration as TPasClassType;
|
|
end;
|
|
if CurClassType.CustomData=nil then
|
|
RaiseInternalError(20160922163611);
|
|
ClassScope:=CurClassType.CustomData as TPasClassScope;
|
|
Result:=TPasDotClassScope.Create;
|
|
Result.Owner:=Self;
|
|
Result.ClassScope:=ClassScope;
|
|
PushScope(Result);
|
|
end;
|
|
|
|
function TPasResolver.PushRecordDotScope(CurRecordType: TPasRecordType
|
|
): TPasDotRecordScope;
|
|
var
|
|
RecScope: TPasRecordScope;
|
|
begin
|
|
RecScope:=CurRecordType.CustomData as TPasRecordScope;
|
|
Result:=TPasDotRecordScope.Create;
|
|
Result.Owner:=Self;
|
|
Result.IdentifierScope:=RecScope;
|
|
PushScope(Result);
|
|
end;
|
|
|
|
function TPasResolver.PushEnumDotScope(CurEnumType: TPasEnumType
|
|
): TPasDotEnumTypeScope;
|
|
var
|
|
EnumScope: TPasEnumTypeScope;
|
|
begin
|
|
EnumScope:=CurEnumType.CustomData as TPasEnumTypeScope;
|
|
Result:=TPasDotEnumTypeScope.Create;
|
|
Result.Owner:=Self;
|
|
Result.IdentifierScope:=EnumScope;
|
|
PushScope(Result);
|
|
end;
|
|
|
|
procedure TPasResolver.ResetSubScopes(out Depth: integer);
|
|
// move all sub scopes from Scopes to SubScopes
|
|
begin
|
|
Depth:=FSubScopeCount;
|
|
while TopScope is TPasSubScope do
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ResetSubScopes moving ',TopScope.ClassName,' ScopeCount=',ScopeCount,' SubScopeCount=',FSubScopeCount);
|
|
{$ENDIF}
|
|
if FSubScopeCount=length(FSubScopes) then
|
|
SetLength(FSubScopes,FSubScopeCount+4);
|
|
FSubScopes[FSubScopeCount]:=TopScope;
|
|
inc(FSubScopeCount);
|
|
dec(FScopeCount);
|
|
FScopes[FScopeCount]:=nil;
|
|
if FScopeCount>0 then
|
|
FTopScope:=FScopes[FScopeCount-1]
|
|
else
|
|
FTopScope:=nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.RestoreSubScopes(Depth: integer);
|
|
// restore sub scopes
|
|
begin
|
|
while FSubScopeCount>Depth do
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.RestoreSubScopes moving ',FSubScopes[FSubScopeCount-1].ClassName,' ScopeCount=',ScopeCount,' SubScopeCount=',FSubScopeCount);
|
|
{$ENDIF}
|
|
if FScopeCount=length(FScopes) then
|
|
SetLength(FScopes,FScopeCount+4);
|
|
dec(FSubScopeCount);
|
|
FScopes[FScopeCount]:=FSubScopes[FSubScopeCount];
|
|
FTopScope:=FScopes[FScopeCount];
|
|
FSubScopes[FSubScopeCount]:=nil;
|
|
inc(FScopeCount);
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.SetLastMsg(const id: int64; MsgType: TMessageType;
|
|
MsgNumber: integer; const Fmt: String; Args: array of const;
|
|
PosEl: TPasElement);
|
|
var
|
|
{$IFDEF VerbosePasResolver}
|
|
s: string;
|
|
{$ENDIF}
|
|
Column, Row: integer;
|
|
begin
|
|
FLastMsgId := id;
|
|
FLastMsgType := MsgType;
|
|
FLastMsgNumber := MsgNumber;
|
|
FLastMsgPattern := Fmt;
|
|
FLastMsg := SafeFormat(Fmt,Args);
|
|
FLastElement := PosEl;
|
|
if PosEl=nil then
|
|
FLastSourcePos:=CurrentParser.Scanner.CurSourcePos
|
|
else
|
|
begin
|
|
FLastSourcePos.FileName:=PosEl.SourceFilename;
|
|
UnmangleSourceLineNumber(PosEl.SourceLinenumber,Row,Column);
|
|
if Row>=0 then
|
|
FLastSourcePos.Row:=Row
|
|
else
|
|
FLastSourcePos.Row:=0;
|
|
if Column>=0 then
|
|
FLastSourcePos.Column:=Column
|
|
else
|
|
FLastSourcePos.Column:=0;
|
|
end;
|
|
CreateMsgArgs(FLastMsgArgs,Args);
|
|
{$IFDEF VerbosePasResolver}
|
|
write('TPasResolver.SetLastMsg ',id,' ',GetElementSourcePosStr(PosEl),' ');
|
|
s:='';
|
|
str(MsgType,s);
|
|
write(s);
|
|
writeln(': [',MsgNumber,'] ',FLastMsg);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TPasResolver.RaiseMsg(const Id: int64; MsgNumber: integer;
|
|
const Fmt: String; Args: array of const; ErrorPosEl: TPasElement);
|
|
var
|
|
E: EPasResolve;
|
|
begin
|
|
SetLastMsg(Id,mtError,MsgNumber,Fmt,Args,ErrorPosEl);
|
|
E:=EPasResolve.Create(FLastMsg);
|
|
E.Id:=Id;
|
|
E.MsgType:=mtError;
|
|
E.MsgNumber:=MsgNumber;
|
|
E.MsgPattern:=Fmt;
|
|
E.PasElement:=ErrorPosEl;
|
|
E.Args:=FLastMsgArgs;
|
|
E.SourcePos:=FLastSourcePos;
|
|
raise E;
|
|
end;
|
|
|
|
procedure TPasResolver.RaiseNotYetImplemented(id: int64; El: TPasElement;
|
|
Msg: string);
|
|
var
|
|
s: String;
|
|
begin
|
|
s:=sNotYetImplemented+' ['+IntToStr(id)+']';
|
|
if Msg<>'' then
|
|
s:=s+' '+Msg;
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.RaiseNotYetImplemented s="',s,'" El=',GetObjName(El));
|
|
{$ENDIF}
|
|
RaiseMsg(id,nNotYetImplemented,s,[GetObjName(El)],El);
|
|
end;
|
|
|
|
procedure TPasResolver.RaiseInternalError(id: int64; const Msg: string);
|
|
begin
|
|
raise Exception.Create('Internal error: ['+IntToStr(id)+'] '+Msg);
|
|
end;
|
|
|
|
procedure TPasResolver.RaiseInvalidScopeForElement(id: int64; El: TPasElement;
|
|
const Msg: string);
|
|
var
|
|
i: Integer;
|
|
s: String;
|
|
begin
|
|
s:='['+IntToStr(id)+'] invalid scope for "'+GetObjName(El)+'": ';
|
|
for i:=0 to ScopeCount-1 do
|
|
begin
|
|
if i>0 then s:=s+',';
|
|
s:=s+Scopes[i].ClassName;
|
|
end;
|
|
if Msg<>'' then
|
|
s:=s+': '+Msg;
|
|
RaiseInternalError(id,s);
|
|
end;
|
|
|
|
procedure TPasResolver.RaiseIdentifierNotFound(id: int64; Identifier: string;
|
|
El: TPasElement);
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.RaiseIdentifierNotFound START "',Identifier,'" ErrorEl=',GetObjName(El));
|
|
WriteScopes;
|
|
{$ENDIF}
|
|
RaiseMsg(id,nIdentifierNotFound,sIdentifierNotFound,[Identifier],El);
|
|
end;
|
|
|
|
procedure TPasResolver.RaiseXExpectedButYFound(id: int64; const X, Y: string;
|
|
El: TPasElement);
|
|
begin
|
|
RaiseMsg(id,nXExpectedButYFound,sXExpectedButYFound,[X,Y],El);
|
|
end;
|
|
|
|
procedure TPasResolver.RaiseConstantExprExp(id: int64; ErrorEl: TPasElement);
|
|
begin
|
|
RaiseMsg(id,nConstantExpressionExpected,sConstantExpressionExpected,[],ErrorEl);
|
|
end;
|
|
|
|
procedure TPasResolver.RaiseIncompatibleTypeDesc(id: int64; MsgNumber: integer;
|
|
const Args: array of const; const GotDesc, ExpDesc: String; ErrorEl: TPasElement);
|
|
|
|
function GetString(ArgNo: integer): string;
|
|
begin
|
|
case Args[ArgNo].VType of
|
|
vtAnsiString: Result:=AnsiString(Args[ArgNo].VAnsiString);
|
|
else
|
|
Result:='invalid param '+IntToStr(Ord(Args[ArgNo].VType));
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
case MsgNumber of
|
|
nIllegalTypeConversionTo:
|
|
RaiseMsg(id,MsgNumber,sIllegalTypeConversionTo,[GotDesc,ExpDesc],ErrorEl);
|
|
nIncompatibleTypesGotExpected:
|
|
RaiseMsg(id,MsgNumber,sIncompatibleTypesGotExpected,[GotDesc,ExpDesc],ErrorEl);
|
|
nIncompatibleTypeArgNo:
|
|
RaiseMsg(id,MsgNumber,sIncompatibleTypeArgNo,[GetString(0),GotDesc,ExpDesc],ErrorEl);
|
|
nIncompatibleTypeArgNoVarParamMustMatchExactly:
|
|
RaiseMsg(id,MsgNumber,sIncompatibleTypeArgNoVarParamMustMatchExactly,
|
|
[GetString(0),GotDesc,ExpDesc],ErrorEl);
|
|
nResultTypeMismatchExpectedButFound:
|
|
RaiseMsg(id,MsgNumber,sResultTypeMismatchExpectedButFound,[GotDesc,ExpDesc],ErrorEl);
|
|
nXExpectedButYFound:
|
|
RaiseMsg(id,MsgNumber,sXExpectedButYFound,[GotDesc,ExpDesc],ErrorEl);
|
|
else
|
|
RaiseInternalError(20170329112911);
|
|
end;
|
|
end;
|
|
|
|
procedure TPasResolver.RaiseIncompatibleType(id: int64; MsgNumber: integer;
|
|
const Args: array of const; GotType, ExpType: TPasType; ErrorEl: TPasElement);
|
|
var
|
|
DescA, DescB: String;
|
|
begin
|
|
DescA:=GetTypeDesc(GotType);
|
|
DescB:=GetTypeDesc(ExpType);
|
|
if DescA=DescB then
|
|
begin
|
|
DescA:=GetTypeDesc(GotType,true);
|
|
DescB:=GetTypeDesc(ExpType,true);
|
|
end;
|
|
RaiseIncompatibleTypeDesc(id,MsgNumber,Args,DescA,DescB,ErrorEl);
|
|
end;
|
|
|
|
procedure TPasResolver.RaiseIncompatibleTypeRes(id: int64; MsgNumber: integer;
|
|
const Args: array of const; const GotType, ExpType: TPasResolverResult;
|
|
ErrorEl: TPasElement);
|
|
|
|
function GetTypeDsc(const R: TPasResolverResult; AddPath: boolean = false): string;
|
|
begin
|
|
Result:=GetTypeDesc(R.TypeEl,AddPath);
|
|
if R.IdentEl=R.TypeEl then
|
|
begin
|
|
if R.TypeEl.ElementTypeName<>'' then
|
|
Result:=R.TypeEl.ElementTypeName+' '+Result
|
|
else
|
|
Result:='type '+Result;
|
|
end;
|
|
end;
|
|
|
|
function GetBaseDecs(const R: TPasResolverResult; AddPath: boolean = false): string;
|
|
begin
|
|
if R.BaseType=btContext then
|
|
Result:=GetTypeDsc(R,AddPath)
|
|
else
|
|
Result:=BaseTypeNames[R.BaseType];
|
|
end;
|
|
|
|
var
|
|
GotDesc, ExpDesc: String;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.RaiseIncompatibleTypeRes Got={',GetResolverResultDesc(GotType),'} Expected={',GetResolverResultDesc(ExpType),'}');
|
|
{$ENDIF}
|
|
if GotType.BaseType<>ExpType.BaseType then
|
|
begin
|
|
GotDesc:=GetBaseDecs(GotType);
|
|
if ExpType.BaseType=btNil then
|
|
ExpDesc:=BaseTypeNames[btPointer]
|
|
else
|
|
ExpDesc:=GetBaseDecs(ExpType);
|
|
if GotDesc=ExpDesc then
|
|
begin
|
|
GotDesc:=GetBaseDecs(GotType,true);
|
|
ExpDesc:=GetBaseDecs(ExpType,true);
|
|
end;
|
|
end
|
|
else if (GotType.TypeEl<>nil) and (ExpType.TypeEl<>nil) then
|
|
begin
|
|
GotDesc:=GetTypeDsc(GotType);
|
|
ExpDesc:=GetTypeDsc(ExpType);
|
|
if GotDesc=ExpDesc then
|
|
begin
|
|
GotDesc:=GetTypeDsc(GotType,true);
|
|
ExpDesc:=GetTypeDsc(ExpType,true);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
GotDesc:=GetResolverResultDescription(GotType,true);
|
|
ExpDesc:=GetResolverResultDescription(ExpType,true);
|
|
if GotDesc=ExpDesc then
|
|
begin
|
|
GotDesc:=GetResolverResultDescription(GotType,false);
|
|
ExpDesc:=GetResolverResultDescription(ExpType,false);
|
|
end;
|
|
end;
|
|
RaiseIncompatibleTypeDesc(id,MsgNumber,Args,GotDesc,ExpDesc,ErrorEl);
|
|
end;
|
|
|
|
procedure TPasResolver.LogMsg(const id: int64; MsgType: TMessageType;
|
|
MsgNumber: integer; const Fmt: String; Args: array of const;
|
|
PosEl: TPasElement);
|
|
begin
|
|
SetLastMsg(id,MsgType,MsgNumber,Fmt,Args,PosEl);
|
|
if Assigned(OnLog) then
|
|
OnLog(Self,FLastMsg)
|
|
else if Assigned(CurrentParser.OnLog) then
|
|
CurrentParser.OnLog(Self,FLastMsg);
|
|
end;
|
|
|
|
function TPasResolver.CheckCallProcCompatibility(ProcType: TPasProcedureType;
|
|
Params: TParamsExpr; RaiseOnError: boolean): integer;
|
|
var
|
|
ProcArgs: TFPList;
|
|
i, ParamCnt, ParamCompatibility: Integer;
|
|
Param: TPasExpr;
|
|
ParamResolved: TPasResolverResult;
|
|
IsVarArgs: Boolean;
|
|
begin
|
|
Result:=cExact;
|
|
ProcArgs:=ProcType.Args;
|
|
// check args
|
|
ParamCnt:=length(Params.Params);
|
|
IsVarArgs:=false;
|
|
i:=0;
|
|
while i<ParamCnt do
|
|
begin
|
|
Param:=Params.Params[i];
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckCallProcCompatibility ',i,'/',ParamCnt);
|
|
{$ENDIF}
|
|
if i<ProcArgs.Count then
|
|
begin
|
|
ParamCompatibility:=CheckParamCompatibility(Param,TPasArgument(ProcArgs[i]),i,RaiseOnError);
|
|
if ParamCompatibility=cIncompatible then
|
|
exit(cIncompatible);
|
|
end
|
|
else
|
|
begin
|
|
IsVarArgs:=IsVarArgs or (ptmVarargs in ProcType.Modifiers);
|
|
if IsVarArgs then
|
|
begin
|
|
ComputeElement(Param,ParamResolved,[],Param);
|
|
if not (rrfReadable in ParamResolved.Flags) then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170318234957,nVariableIdentifierExpected,
|
|
sVariableIdentifierExpected,[],Param);
|
|
exit(cIncompatible);
|
|
end;
|
|
ParamCompatibility:=cExact;
|
|
end
|
|
else
|
|
begin
|
|
// too many arguments
|
|
if RaiseOnError then
|
|
RaiseMsg(20170216152408,nWrongNumberOfParametersForCallTo,
|
|
sWrongNumberOfParametersForCallTo,[GetProcDesc(ProcType)],Param);
|
|
exit(cIncompatible);
|
|
end;
|
|
end;
|
|
inc(Result,ParamCompatibility);
|
|
inc(i);
|
|
end;
|
|
if (i<ProcArgs.Count) and (TPasArgument(ProcArgs[i]).ValueExpr=nil) then
|
|
begin
|
|
// not enough arguments
|
|
if RaiseOnError then
|
|
// ToDo: position cursor on identifier
|
|
RaiseMsg(20170216152410,nWrongNumberOfParametersForCallTo,
|
|
sWrongNumberOfParametersForCallTo,[GetProcDesc(ProcType)],Params.Value);
|
|
exit(cIncompatible);
|
|
end;
|
|
end;
|
|
|
|
function TPasResolver.CheckCallPropertyCompatibility(PropEl: TPasProperty;
|
|
Params: TParamsExpr; RaiseOnError: boolean): integer;
|
|
var
|
|
PropArg: TPasArgument;
|
|
ArgNo, ParamComp: Integer;
|
|
Param: TPasExpr;
|
|
begin
|
|
Result:=cExact;
|
|
if PropEl.Args.Count<length(Params.Params) then
|
|
begin
|
|
if not RaiseOnError then exit(cIncompatible);
|
|
RaiseMsg(20170216152412,nWrongNumberOfParametersForCallTo,sWrongNumberOfParametersForCallTo,
|
|
[PropEl.Name],Params)
|
|
end
|
|
else if PropEl.Args.Count>length(Params.Params) then
|
|
begin
|
|
if not RaiseOnError then exit(cIncompatible);
|
|
RaiseMsg(20170216152413,nMissingParameterX,sMissingParameterX,
|
|
[TPasArgument(PropEl.Args[length(Params.Params)]).Name],Params);
|
|
end;
|
|
for ArgNo:=0 to PropEl.Args.Count-1 do
|
|
begin
|
|
PropArg:=TPasArgument(PropEl.Args[ArgNo]);
|
|
Param:=Params.Params[ArgNo];
|
|
ParamComp:=CheckParamCompatibility(Param,PropArg,ArgNo,RaiseOnError);
|
|
if ParamComp=cIncompatible then
|
|
exit(cIncompatible);
|
|
inc(Result,ParamComp);
|
|
end;
|
|
end;
|
|
|
|
function TPasResolver.CheckCallArrayCompatibility(ArrayEl: TPasArrayType;
|
|
Params: TParamsExpr; RaiseOnError: boolean): integer;
|
|
var
|
|
ArgNo: Integer;
|
|
Param: TPasExpr;
|
|
ParamResolved: TPasResolverResult;
|
|
|
|
procedure GetNextParam;
|
|
begin
|
|
if ArgNo>=length(Params.Params) then
|
|
RaiseMsg(20170216152415,nWrongNumberOfParametersForArray,sWrongNumberOfParametersForArray,
|
|
[],Params);
|
|
Param:=Params.Params[ArgNo];
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
inc(ArgNo);
|
|
end;
|
|
|
|
var
|
|
DimNo: integer;
|
|
RangeResolved: TPasResolverResult;
|
|
bt: TResolverBaseType;
|
|
NextType: TPasType;
|
|
begin
|
|
ArgNo:=0;
|
|
repeat
|
|
if length(ArrayEl.Ranges)=0 then
|
|
begin
|
|
// dynamic/open array -> needs exactly one integer
|
|
GetNextParam;
|
|
if (not (rrfReadable in ParamResolved.Flags))
|
|
or not (ParamResolved.BaseType in btAllInteger) then
|
|
exit(CheckRaiseTypeArgNo(20170216152417,ArgNo,Param,ParamResolved,'integer',RaiseOnError));
|
|
end
|
|
else
|
|
begin
|
|
// static array
|
|
for DimNo:=0 to length(ArrayEl.Ranges)-1 do
|
|
begin
|
|
GetNextParam;
|
|
ComputeElement(ArrayEl.Ranges[DimNo],RangeResolved,[]);
|
|
bt:=RangeResolved.BaseType;
|
|
if bt=btRange then
|
|
bt:=RangeResolved.SubType;
|
|
if not (rrfReadable in ParamResolved.Flags) then
|
|
begin
|
|
if not RaiseOnError then exit(cIncompatible);
|
|
RaiseIncompatibleTypeRes(20170216152421,nIncompatibleTypeArgNo,
|
|
[IntToStr(ArgNo)],ParamResolved,RangeResolved,Param);
|
|
end;
|
|
if (bt in btAllBooleans) and (ParamResolved.BaseType in btAllBooleans) then
|
|
continue
|
|
else if (bt in btAllInteger) and (ParamResolved.BaseType in btAllInteger) then
|
|
continue
|
|
else if (bt in [btChar,btWideChar]) and (ParamResolved.BaseType in [btChar,btWideChar]) then
|
|
continue
|
|
else if (bt=btContext) and (ParamResolved.BaseType=btContext) then
|
|
begin
|
|
if (RangeResolved.TypeEl.ClassType=TPasEnumType)
|
|
and (RangeResolved.TypeEl=ParamResolved.TypeEl) then
|
|
continue;
|
|
end;
|
|
// incompatible
|
|
if not RaiseOnError then exit(cIncompatible);
|
|
RaiseIncompatibleTypeRes(20170216152422,nIncompatibleTypeArgNo,
|
|
[IntToStr(ArgNo)],ParamResolved,RangeResolved,Param);
|
|
end;
|
|
end;
|
|
if ArgNo=length(Params.Params) then exit(cExact);
|
|
|
|
// there are more parameters -> continue in sub array
|
|
NextType:=ResolveAliasType(ArrayEl.ElType);
|
|
if NextType.ClassType<>TPasArrayType then
|
|
RaiseMsg(20170216152424,nWrongNumberOfParametersForArray,sWrongNumberOfParametersForArray,
|
|
[],Params);
|
|
ArrayEl:=TPasArrayType(NextType);
|
|
until false;
|
|
end;
|
|
|
|
function TPasResolver.CheckOverloadProcCompatibility(Proc1, Proc2: TPasProcedure
|
|
): boolean;
|
|
// returns if number and type of arguments fit
|
|
// does not check calling convention
|
|
var
|
|
ProcArgs1, ProcArgs2: TFPList;
|
|
i: Integer;
|
|
begin
|
|
Result:=false;
|
|
ProcArgs1:=Proc1.ProcType.Args;
|
|
ProcArgs2:=Proc2.ProcType.Args;
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckOverloadProcCompatibility START Count=',ProcArgs1.Count,' ',ProcArgs2.Count);
|
|
{$ENDIF}
|
|
// check args
|
|
if ProcArgs1.Count<>ProcArgs2.Count then
|
|
exit;
|
|
for i:=0 to ProcArgs1.Count-1 do
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckOverloadProcCompatibility ',i,'/',ProcArgs1.Count);
|
|
{$ENDIF}
|
|
if not CheckProcArgCompatibility(TPasArgument(ProcArgs1[i]),TPasArgument(ProcArgs2[i])) then
|
|
exit;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TPasResolver.CheckProcTypeCompatibility(Proc1,
|
|
Proc2: TPasProcedureType; ErrorEl: TPasElement; RaiseOnIncompatible: boolean
|
|
): boolean;
|
|
// if RaiseOnIncompatible=true, then Expected=Proc1 Actual=Proc2
|
|
|
|
function ModifierError(const Modifier: string): boolean;
|
|
begin
|
|
Result:=false;
|
|
if not RaiseOnIncompatible then exit;
|
|
RaiseMsg(20170402112049,nXModifierMismatchY,sXModifierMismatchY,
|
|
[Proc1.ElementTypeName,Modifier],ErrorEl);
|
|
end;
|
|
|
|
var
|
|
ProcArgs1, ProcArgs2: TFPList;
|
|
i: Integer;
|
|
Result1Resolved, Result2Resolved: TPasResolverResult;
|
|
ExpectedArg, ActualArg: TPasArgument;
|
|
begin
|
|
Result:=false;
|
|
if Proc1.ClassType<>Proc2.ClassType then
|
|
begin
|
|
if RaiseOnIncompatible then
|
|
RaiseXExpectedButYFound(20170402112353,Proc1.ElementTypeName,Proc2.ElementTypeName,ErrorEl);
|
|
exit;
|
|
end;
|
|
if Proc1.IsNested<>Proc2.IsNested then
|
|
exit(ModifierError(ProcTypeModifiers[ptmIsNested]));
|
|
if Proc1.IsOfObject<>Proc2.IsOfObject then
|
|
begin
|
|
if (proProcTypeWithoutIsNested in Options) then
|
|
exit(ModifierError(ProcTypeModifiers[ptmOfObject]))
|
|
else if Proc1.IsNested then
|
|
// "is nested" can handle both, proc and method.
|
|
else
|
|
exit(ModifierError(ProcTypeModifiers[ptmOfObject]))
|
|
end;
|
|
if Proc1.CallingConvention<>Proc2.CallingConvention then
|
|
begin
|
|
if RaiseOnIncompatible then
|
|
RaiseMsg(20170402112253,nCallingConventionMismatch,sCallingConventionMismatch,
|
|
[],ErrorEl);
|
|
exit;
|
|
end;
|
|
ProcArgs1:=Proc1.Args;
|
|
ProcArgs2:=Proc2.Args;
|
|
if ProcArgs1.Count<>ProcArgs2.Count then exit;
|
|
for i:=0 to ProcArgs1.Count-1 do
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckProcAssignCompatibility ',i,'/',ProcArgs1.Count);
|
|
{$ENDIF}
|
|
ExpectedArg:=TPasArgument(ProcArgs1[i]);
|
|
ActualArg:=TPasArgument(ProcArgs2[i]);
|
|
if not CheckProcArgCompatibility(ExpectedArg,ActualArg) then
|
|
begin
|
|
if RaiseOnIncompatible then
|
|
begin
|
|
if ExpectedArg.Access<>ActualArg.Access then
|
|
RaiseMsg(20170404151541,nIncompatibleTypeArgNo,sIncompatibleTypeArgNo,
|
|
[IntToStr(i+1),'access modifier '+AccessDescriptions[ActualArg.Access],
|
|
AccessDescriptions[ExpectedArg.Access]],
|
|
ErrorEl);
|
|
RaiseIncompatibleType(20170404151538,nIncompatibleTypeArgNo,
|
|
[IntToStr(i+1)],ExpectedArg.ArgType,ActualArg.ArgType,ErrorEl);
|
|
end;
|
|
exit;
|
|
end;
|
|
end;
|
|
if Proc1 is TPasFunctionType then
|
|
begin
|
|
ComputeElement(TPasFunctionType(Proc1).ResultEl.ResultType,Result1Resolved,[rcType]);
|
|
ComputeElement(TPasFunctionType(Proc2).ResultEl.ResultType,Result2Resolved,[rcType]);
|
|
if (Result1Resolved.BaseType<>Result2Resolved.BaseType)
|
|
or not IsSameType(Result1Resolved.TypeEl,Result2Resolved.TypeEl) then
|
|
begin
|
|
if RaiseOnIncompatible then
|
|
RaiseIncompatibleTypeRes(20170402112648,nResultTypeMismatchExpectedButFound,
|
|
[],Result1Resolved,Result2Resolved,ErrorEl);
|
|
exit;
|
|
end;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TPasResolver.CheckProcArgCompatibility(Arg1, Arg2: TPasArgument): boolean;
|
|
begin
|
|
Result:=false;
|
|
|
|
// check access: var, const, ...
|
|
if Arg1.Access<>Arg2.Access then exit;
|
|
|
|
// check untyped
|
|
if Arg1.ArgType=nil then
|
|
exit(Arg2.ArgType=nil);
|
|
if Arg2.ArgType=nil then exit;
|
|
|
|
Result:=CheckProcArgTypeCompatibility(Arg1.ArgType,Arg2.ArgType);
|
|
end;
|
|
|
|
function TPasResolver.CheckProcArgTypeCompatibility(Arg1, Arg2: TPasType
|
|
): boolean;
|
|
var
|
|
Arg1Resolved, Arg2Resolved: TPasResolverResult;
|
|
C: TClass;
|
|
Arr1, Arr2: TPasArrayType;
|
|
begin
|
|
ComputeElement(Arg1,Arg1Resolved,[rcType]);
|
|
ComputeElement(Arg2,Arg2Resolved,[rcType]);
|
|
{$IFDEF VerbosePasResolver}
|
|
//writeln('TPasResolver.CheckProcArgTypeCompatibility Arg1=',GetResolverResultDesc(Arg1Resolved),' Arg2=',GetResolverResultDesc(Arg2Resolved));
|
|
{$ENDIF}
|
|
|
|
if (Arg1Resolved.BaseType<>Arg2Resolved.BaseType)
|
|
or (Arg1Resolved.TypeEl=nil)
|
|
or (Arg2Resolved.TypeEl=nil) then
|
|
exit(false);
|
|
if (Arg1Resolved.BaseType=Arg2Resolved.BaseType)
|
|
and IsSameType(Arg1Resolved.TypeEl,Arg2Resolved.TypeEl) then
|
|
exit(true);
|
|
C:=Arg1Resolved.TypeEl.ClassType;
|
|
if (C=TPasArrayType) and (Arg2Resolved.TypeEl.ClassType=TPasArrayType) then
|
|
begin
|
|
Arr1:=TPasArrayType(Arg1Resolved.TypeEl);
|
|
Arr2:=TPasArrayType(Arg2Resolved.TypeEl);
|
|
if length(Arr1.Ranges)<>length(Arr2.Ranges) then
|
|
exit(false);
|
|
if length(Arr1.Ranges)>0 then
|
|
RaiseNotYetImplemented(20170328093733,Arr1.Ranges[0],'anonymous static array');
|
|
Result:=CheckProcArgTypeCompatibility(Arr1.ElType,Arr2.ElType);
|
|
exit;
|
|
end;
|
|
|
|
Result:=false;
|
|
end;
|
|
|
|
function TPasResolver.CheckCanBeLHS(const ResolvedEl: TPasResolverResult;
|
|
ErrorOnFalse: boolean; ErrorEl: TPasElement): boolean;
|
|
var
|
|
El: TPasElement;
|
|
begin
|
|
Result:=false;
|
|
El:=ResolvedEl.IdentEl;
|
|
if El=nil then
|
|
begin
|
|
if ErrorOnFalse then
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckCanBeLHS ',GetResolverResultDesc(ResolvedEl));
|
|
{$ENDIF}
|
|
if (ResolvedEl.TypeEl<>nil) and (ResolvedEl.ExprEl<>nil) then
|
|
RaiseXExpectedButYFound(20170216152727,'identifier',ResolvedEl.TypeEl.ElementTypeName,ResolvedEl.ExprEl)
|
|
else
|
|
RaiseMsg(20170216152426,nVariableIdentifierExpected,sVariableIdentifierExpected,[],ErrorEl);
|
|
end;
|
|
exit;
|
|
end;
|
|
if [rrfWritable,rrfAssignable]*ResolvedEl.Flags<>[] then
|
|
exit(true);
|
|
// not writable
|
|
if not ErrorOnFalse then exit;
|
|
if ResolvedEl.IdentEl is TPasProperty then
|
|
RaiseMsg(20170216152427,nPropertyNotWritable,sPropertyNotWritable,[],ErrorEl)
|
|
else
|
|
RaiseMsg(20170216152429,nVariableIdentifierExpected,sVariableIdentifierExpected,[],ErrorEl);
|
|
end;
|
|
|
|
function TPasResolver.CheckAssignCompatibility(const LHS, RHS: TPasElement;
|
|
RaiseOnIncompatible: boolean): integer;
|
|
var
|
|
LeftResolved, RightResolved: TPasResolverResult;
|
|
Flags: TPasResolverComputeFlags;
|
|
IsProcType: Boolean;
|
|
begin
|
|
ComputeElement(LHS,LeftResolved,[rcNoImplicitProc]);
|
|
Flags:=[];
|
|
IsProcType:=IsProcedureType(LeftResolved,true);
|
|
if IsProcType then
|
|
if msDelphi in CurrentParser.CurrentModeswitches then
|
|
Include(Flags,rcNoImplicitProc)
|
|
else
|
|
Include(Flags,rcNoImplicitProcType);
|
|
ComputeElement(RHS,RightResolved,Flags);
|
|
Result:=CheckAssignResCompatibility(LeftResolved,RightResolved,RHS,RaiseOnIncompatible);
|
|
end;
|
|
|
|
function TPasResolver.CheckAssignResCompatibility(const LHS,
|
|
RHS: TPasResolverResult; ErrorEl: TPasElement; RaiseOnIncompatible: boolean
|
|
): integer;
|
|
var
|
|
TypeEl: TPasType;
|
|
Handled: Boolean;
|
|
C: TClass;
|
|
begin
|
|
// check if the RHS can be converted to LHS
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckAssignResCompatibility START LHS='+GetResolverResultDesc(LHS)+' RHS='+GetResolverResultDesc(RHS));
|
|
{$ENDIF}
|
|
Result:=-1;
|
|
|
|
Handled:=false;
|
|
Result:=CheckAssignCompatibilityCustom(LHS,RHS,ErrorEl,RaiseOnIncompatible,Handled);
|
|
if Handled and (Result>=cExact) and (Result<cIncompatible) then
|
|
exit;
|
|
|
|
if not Handled then
|
|
begin
|
|
if LHS.TypeEl=nil then
|
|
begin
|
|
if LHS.BaseType=btUntyped then
|
|
begin
|
|
// untyped parameter
|
|
Result:=cExact+1;
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20160922163631,LHS.IdentEl);
|
|
end
|
|
else if LHS.BaseType=RHS.BaseType then
|
|
begin
|
|
if LHS.BaseType=btContext then
|
|
exit(CheckAssignCompatibilityUserType(LHS,RHS,ErrorEl,RaiseOnIncompatible))
|
|
else
|
|
Result:=cExact; // same base type, maybe not same type name (e.g. longint and integer)
|
|
end
|
|
else if (LHS.BaseType in btAllInteger)
|
|
and (RHS.BaseType in btAllInteger) then
|
|
Result:=cExact+1
|
|
else if (LHS.BaseType in btAllBooleans)
|
|
and (RHS.BaseType in btAllBooleans) then
|
|
Result:=cExact+1
|
|
else if (LHS.BaseType in btAllStringAndChars)
|
|
and (RHS.BaseType in btAllStringAndChars) then
|
|
Result:=cExact+1
|
|
else if (LHS.BaseType in btAllFloats)
|
|
and (RHS.BaseType in btAllFloats+btAllInteger) then
|
|
Result:=cExact+1
|
|
else if LHS.BaseType=btNil then
|
|
begin
|
|
if RaiseOnIncompatible then
|
|
RaiseMsg(20170216152431,nCantAssignValuesToAnAddress,sCantAssignValuesToAnAddress,
|
|
[],ErrorEl);
|
|
exit(cIncompatible);
|
|
end
|
|
else if LHS.BaseType in [btRange,btSet,btModule,btArray,btProc] then
|
|
begin
|
|
if RaiseOnIncompatible then
|
|
RaiseMsg(20170216152432,nIllegalExpression,sIllegalExpression,[],ErrorEl);
|
|
exit(cIncompatible);
|
|
end
|
|
else if (LHS.IdentEl=nil) and (LHS.ExprEl=nil) then
|
|
begin
|
|
if RaiseOnIncompatible then
|
|
RaiseMsg(20170216152434,nIllegalExpression,sIllegalExpression,[],ErrorEl);
|
|
exit(cIncompatible);
|
|
end
|
|
else if RHS.BaseType=btNil then
|
|
begin
|
|
if LHS.BaseType=btPointer then
|
|
Result:=cExact
|
|
else if LHS.BaseType=btContext then
|
|
begin
|
|
TypeEl:=LHS.TypeEl;
|
|
C:=TypeEl.ClassType;
|
|
if (C=TPasClassType)
|
|
or (C=TPasClassOfType)
|
|
or (C=TPasPointerType)
|
|
or C.InheritsFrom(TPasProcedureType)
|
|
or IsDynArray(TypeEl) then
|
|
Result:=cExact;
|
|
end;
|
|
end
|
|
else if RHS.BaseType=btSet then
|
|
begin
|
|
if (LHS.BaseType=btSet) then
|
|
begin
|
|
if RHS.TypeEl=nil then
|
|
Result:=cExact // empty set
|
|
else if (LHS.SubType=RHS.SubType) and (LHS.SubType in (btAllBooleans+btAllInteger+[btChar])) then
|
|
Result:=cExact
|
|
else if ((LHS.SubType in btAllBooleans) and (RHS.SubType in btAllBooleans))
|
|
or ((LHS.SubType in btAllInteger) and (RHS.SubType in btAllInteger)) then
|
|
Result:=cExact+1
|
|
else if (LHS.SubType=btContext) and (LHS.TypeEl is TPasEnumType)
|
|
and (LHS.TypeEl=RHS.TypeEl) then
|
|
Result:=cExact;
|
|
end;
|
|
end
|
|
else if RHS.BaseType=btProc then
|
|
begin
|
|
if (msDelphi in CurrentParser.CurrentModeswitches)
|
|
and (LHS.TypeEl is TPasProcedureType)
|
|
and (RHS.IdentEl is TPasProcedure) then
|
|
begin
|
|
// for example ProcVar:=Proc
|
|
if CheckProcTypeCompatibility(TPasProcedureType(LHS.TypeEl),
|
|
TPasProcedure(RHS.IdentEl).ProcType,ErrorEl,RaiseOnIncompatible) then
|
|
Result:=cExact;
|
|
end;
|
|
end
|
|
else if LHS.BaseType=btPointer then
|
|
begin
|
|
if RHS.BaseType=btPointer then
|
|
begin
|
|
if IsBaseType(LHS.TypeEl,btPointer) then
|
|
Result:=cExact // btPointer can take any pointer
|
|
else if IsBaseType(RHS.TypeEl,btPointer) then
|
|
Result:=cExact+1 // any pointer can take a btPointer
|
|
else if IsSameType(LHS.TypeEl,RHS.TypeEl) then
|
|
Result:=cExact // pointer of same type
|
|
else if (LHS.TypeEl.ClassType=TPasPointerType)
|
|
and (RHS.TypeEl.ClassType=TPasPointerType) then
|
|
Result:=CheckAssignCompatibility(TPasPointerType(LHS.TypeEl).DestType,
|
|
TPasPointerType(RHS.TypeEl).DestType,RaiseOnIncompatible);
|
|
end
|
|
else if IsBaseType(LHS.TypeEl,btPointer) then
|
|
begin
|
|
if RHS.BaseType=btContext then
|
|
begin
|
|
C:=RHS.TypeEl.ClassType;
|
|
if C=TPasClassType then
|
|
exit(cExact) // class type or class instance
|
|
else if C=TPasClassOfType then
|
|
Result:=cExact
|
|
else if C=TPasArrayType then
|
|
begin
|
|
if IsDynArray(RHS.TypeEl) then
|
|
Result:=cExact;
|
|
end
|
|
else if (C=TPasProcedureType) or (C=TPasFunctionType) then
|
|
Result:=cExact+1;
|
|
end;
|
|
end;
|
|
end
|
|
else if (LHS.BaseType=btContext) and (LHS.TypeEl is TPasArrayType) then
|
|
Result:=CheckAssignCompatibilityArrayType(LHS,RHS,ErrorEl,RaiseOnIncompatible);
|
|
end;
|
|
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckAssignResCompatibility incompatible LHS='+GetResolverResultDesc(LHS)+' RHS='+GetResolverResultDesc(RHS));
|
|
{$ENDIF}
|
|
if (Result>=0) and (Result<cIncompatible) then
|
|
begin
|
|
// type fits -> check readable
|
|
if not (rrfReadable in RHS.Flags) then
|
|
begin
|
|
if RaiseOnIncompatible then
|
|
RaiseMsg(20170318235637,nVariableIdentifierExpected,
|
|
sVariableIdentifierExpected,[],ErrorEl);
|
|
exit(cIncompatible);
|
|
end;
|
|
exit;
|
|
end;
|
|
|
|
// incompatible
|
|
if not RaiseOnIncompatible then
|
|
exit(cIncompatible);
|
|
|
|
// create error messages
|
|
RaiseIncompatibleTypeRes(20170216152437,nIncompatibleTypesGotExpected,
|
|
[],RHS,LHS,ErrorEl);
|
|
end;
|
|
|
|
function TPasResolver.CheckEqualElCompatibility(Left, Right: TPasElement;
|
|
ErrorEl: TPasElement; RaiseOnIncompatible: boolean): integer;
|
|
// check if the RightResolved is type compatible to LeftResolved
|
|
var
|
|
Flags: TPasResolverComputeFlags;
|
|
LeftResolved, RightResolved: TPasResolverResult;
|
|
LeftErrorEl, RightErrorEl: TPasElement;
|
|
begin
|
|
Result:=cIncompatible;
|
|
// Delphi resolves both sides, so it forbids "if procvar=procvar then"
|
|
// FPC is more clever. It supports "if procvar=@proc then", "function=value"
|
|
if msDelphi in CurrentParser.CurrentModeswitches then
|
|
Flags:=[]
|
|
else
|
|
Flags:=[rcNoImplicitProcType];
|
|
ComputeElement(Left,LeftResolved,Flags);
|
|
if not (msDelphi in CurrentParser.CurrentModeswitches) then
|
|
begin
|
|
if LeftResolved.BaseType=btNil then
|
|
Flags:=[rcNoImplicitProcType]
|
|
else if IsProcedureType(LeftResolved,true) then
|
|
Flags:=[rcNoImplicitProcType]
|
|
else
|
|
Flags:=[];
|
|
end;
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckEqualElCompatibility Left=',GetResolverResultDesc(LeftResolved),' Flags=',dbgs(Flags),' Delphi=',msDelphi in CurrentParser.CurrentModeswitches);
|
|
{$ENDIF}
|
|
ComputeElement(Right,RightResolved,Flags);
|
|
if ErrorEl=nil then
|
|
begin
|
|
LeftErrorEl:=Left;
|
|
RightErrorEl:=Right;
|
|
end
|
|
else
|
|
begin
|
|
LeftErrorEl:=ErrorEl;
|
|
RightErrorEl:=ErrorEl;
|
|
end;
|
|
Result:=CheckEqualResCompatibility(LeftResolved,RightResolved,LeftErrorEl,
|
|
RaiseOnIncompatible,RightErrorEl);
|
|
end;
|
|
|
|
function TPasResolver.CheckEqualResCompatibility(const LHS,
|
|
RHS: TPasResolverResult; LErrorEl: TPasElement; RaiseOnIncompatible: boolean;
|
|
RErrorEl: TPasElement): integer;
|
|
var
|
|
TypeEl: TPasType;
|
|
ok: Boolean;
|
|
begin
|
|
Result:=cIncompatible;
|
|
if RErrorEl=nil then RErrorEl:=LErrorEl;
|
|
// check if the RHS is type compatible to LHS
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckEqualCompatibility LHS=',GetResolverResultDesc(LHS),' RHS=',GetResolverResultDesc(RHS));
|
|
{$ENDIF}
|
|
if not (rrfReadable in LHS.Flags) then
|
|
begin
|
|
ok:=false;
|
|
if (LHS.BaseType=btContext) and (LHS.TypeEl.ClassType=TPasClassType)
|
|
and (LHS.IdentEl=LHS.TypeEl) then
|
|
begin
|
|
if RHS.BaseType=btNil then
|
|
ok:=true
|
|
else if (RHS.BaseType=btContext) and (RHS.TypeEl.ClassType=TPasClassOfType)
|
|
and (rrfReadable in RHS.Flags) then
|
|
// for example if TImage=ImageClass then
|
|
ok:=true;
|
|
end;
|
|
if not ok then
|
|
RaiseMsg(20170216152438,nNotReadable,sNotReadable,[],LErrorEl);
|
|
end;
|
|
if not (rrfReadable in RHS.Flags) then
|
|
begin
|
|
ok:=false;
|
|
if (RHS.BaseType=btContext) and (RHS.TypeEl.ClassType=TPasClassType)
|
|
and (RHS.IdentEl=RHS.TypeEl) then
|
|
begin
|
|
if LHS.BaseType=btNil then
|
|
ok:=true
|
|
else if (LHS.BaseType=btContext) and (LHS.TypeEl.ClassType=TPasClassOfType)
|
|
and (rrfReadable in LHS.Flags) then
|
|
// for example if ImageClass=TImage then
|
|
ok:=true;
|
|
end;
|
|
if not ok then
|
|
RaiseMsg(20170216152440,nNotReadable,sNotReadable,[],RErrorEl);
|
|
end;
|
|
|
|
if (LHS.BaseType=btCustom) or (RHS.BaseType=btCustom) then
|
|
begin
|
|
Result:=CheckEqualCompatibilityCustomType(LHS,RHS,LErrorEl,RaiseOnIncompatible);
|
|
if (Result=cIncompatible) and RaiseOnIncompatible then
|
|
RaiseIncompatibleTypeRes(20170330010727,nIncompatibleTypesGotExpected,
|
|
[],RHS,LHS,LErrorEl);
|
|
exit;
|
|
end
|
|
else if LHS.BaseType=RHS.BaseType then
|
|
begin
|
|
if LHS.BaseType=btContext then
|
|
exit(CheckEqualCompatibilityUserType(LHS,RHS,LErrorEl,RaiseOnIncompatible))
|
|
else
|
|
exit(cExact); // same base type, maybe not same type name (e.g. longint and integer)
|
|
end
|
|
else if (LHS.BaseType in btAllInteger+btAllFloats)
|
|
and (RHS.BaseType in btAllInteger+btAllFloats) then
|
|
exit(cExact+1) // ToDo: range check for Expr
|
|
else if (LHS.BaseType in btAllBooleans)
|
|
and (RHS.BaseType in btAllBooleans) then
|
|
exit(cExact+1)
|
|
else if (LHS.BaseType in btAllStringAndChars)
|
|
and (RHS.BaseType in btAllStringAndChars) then
|
|
exit(cExact+1)
|
|
else if LHS.BaseType=btNil then
|
|
begin
|
|
if RHS.BaseType in [btPointer,btNil] then
|
|
exit(cExact)
|
|
else if RHS.BaseType=btContext then
|
|
begin
|
|
TypeEl:=RHS.TypeEl;
|
|
if (TypeEl.ClassType=TPasClassType)
|
|
or (TypeEl.ClassType=TPasClassOfType)
|
|
or (TypeEl.ClassType=TPasPointerType)
|
|
or (TypeEl is TPasProcedureType)
|
|
or IsDynArray(TypeEl) then
|
|
exit(cExact);
|
|
end;
|
|
if RaiseOnIncompatible then
|
|
RaiseIncompatibleTypeRes(20170216152442,nIncompatibleTypesGotExpected,
|
|
[],RHS,LHS,RErrorEl)
|
|
else
|
|
exit(cIncompatible);
|
|
end
|
|
else if RHS.BaseType=btNil then
|
|
begin
|
|
if LHS.BaseType=btPointer then
|
|
exit(cExact)
|
|
else if LHS.BaseType=btContext then
|
|
begin
|
|
TypeEl:=LHS.TypeEl;
|
|
if (TypeEl.ClassType=TPasClassType)
|
|
or (TypeEl.ClassType=TPasClassOfType)
|
|
or (TypeEl.ClassType=TPasPointerType)
|
|
or (TypeEl is TPasProcedureType)
|
|
or IsDynArray(TypeEl) then
|
|
exit(cExact);
|
|
end;
|
|
if RaiseOnIncompatible then
|
|
RaiseIncompatibleTypeRes(20170216152444,nIncompatibleTypesGotExpected,
|
|
[],LHS,RHS,LErrorEl)
|
|
else
|
|
exit(cIncompatible);
|
|
end
|
|
else if LHS.BaseType=btSet then
|
|
begin
|
|
if RHS.BaseType=btSet then
|
|
begin
|
|
if LHS.TypeEl=nil then
|
|
exit(cExact); // empty set
|
|
if RHS.TypeEl=nil then
|
|
exit(cExact); // empty set
|
|
if (LHS.SubType=RHS.SubType) and (LHS.SubType in (btAllBooleans+btAllInteger+[btChar])) then
|
|
exit(cExact);
|
|
if ((LHS.SubType in btAllBooleans) and (RHS.SubType in btAllBooleans))
|
|
or ((LHS.SubType in btAllInteger) and (RHS.SubType in btAllInteger)) then
|
|
exit(cExact+1);
|
|
if (LHS.SubType=btContext) and (LHS.TypeEl is TPasEnumType)
|
|
and (LHS.TypeEl=RHS.TypeEl) then
|
|
exit(cExact);
|
|
if RaiseOnIncompatible then
|
|
RaiseMsg(20170216152446,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected,
|
|
['set of '+BaseTypeNames[LHS.SubType],'set of '+BaseTypeNames[RHS.SubType]],LErrorEl)
|
|
else
|
|
exit(cIncompatible);
|
|
end;
|
|
end
|
|
else if RaiseOnIncompatible then
|
|
RaiseMsg(20170216152449,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected,
|
|
[BaseTypeNames[LHS.BaseType],BaseTypeNames[RHS.BaseType]],LErrorEl)
|
|
else
|
|
exit(cIncompatible);
|
|
RaiseNotYetImplemented(20161007101041,LErrorEl,'LHS='+GetResolverResultDesc(LHS)+' RHS='+GetResolverResultDesc(RHS));
|
|
end;
|
|
|
|
function TPasResolver.ResolvedElHasValue(const ResolvedEl: TPasResolverResult
|
|
): boolean;
|
|
begin
|
|
if not (rrfReadable in ResolvedEl.Flags) then
|
|
Result:=false
|
|
else if ResolvedEl.ExprEl<>nil then
|
|
Result:=true
|
|
else if (ResolvedEl.IdentEl<>nil) then
|
|
Result:=not (ResolvedEl.IdentEl is TPasType)
|
|
else
|
|
Result:=false;
|
|
end;
|
|
|
|
function TPasResolver.ResolvedElCanBeVarParam(
|
|
const ResolvedEl: TPasResolverResult): boolean;
|
|
begin
|
|
Result:=false;
|
|
if [rrfReadable,rrfWritable]*ResolvedEl.Flags<>[rrfReadable,rrfWritable] then
|
|
exit;
|
|
if ResolvedEl.IdentEl=nil then exit;
|
|
if ResolvedEl.IdentEl.ClassType=TPasVariable then
|
|
exit(true);
|
|
if (ResolvedEl.IdentEl.ClassType=TPasArgument) then
|
|
begin
|
|
Result:=(TPasArgument(ResolvedEl.IdentEl).Access in [argDefault, argVar, argOut]);
|
|
exit;
|
|
end;
|
|
if ResolvedEl.IdentEl.ClassType=TPasResultElement then
|
|
exit(true);
|
|
if (ResolvedEl.IdentEl.ClassType=TPasConst) then
|
|
begin
|
|
// typed const are writable
|
|
Result:=(TPasConst(ResolvedEl.IdentEl).VarType<>nil);
|
|
exit;
|
|
end;
|
|
if (proPropertyAsVarParam in Options)
|
|
and (ResolvedEl.IdentEl.ClassType=TPasProperty) then
|
|
exit(true);
|
|
end;
|
|
|
|
function TPasResolver.ResolvedElIsClassInstance(
|
|
const ResolvedEl: TPasResolverResult): boolean;
|
|
begin
|
|
Result:=false;
|
|
if ResolvedEl.BaseType<>btContext then exit;
|
|
if ResolvedEl.TypeEl=nil then exit;
|
|
if ResolvedEl.TypeEl.ClassType<>TPasClassType then exit;
|
|
if (ResolvedEl.IdentEl is TPasVariable)
|
|
or (ResolvedEl.IdentEl.ClassType=TPasArgument)
|
|
or (ResolvedEl.IdentEl.ClassType=TPasResultElement) then
|
|
exit(true);
|
|
end;
|
|
|
|
function TPasResolver.GetPasPropertyType(El: TPasProperty): TPasType;
|
|
begin
|
|
Result:=nil;
|
|
while El<>nil do
|
|
begin
|
|
if El.VarType<>nil then
|
|
exit(El.VarType);
|
|
El:=GetPasPropertyAncestor(El);
|
|
end;
|
|
end;
|
|
|
|
function TPasResolver.GetPasPropertyAncestor(El: TPasProperty): TPasProperty;
|
|
begin
|
|
Result:=nil;
|
|
if El=nil then exit;
|
|
if El.CustomData=nil then exit;
|
|
Result:=TPasPropertyScope(El.CustomData).AncestorProp;
|
|
end;
|
|
|
|
function TPasResolver.GetPasPropertyGetter(El: TPasProperty): TPasElement;
|
|
// search the member variable or getter function of a property
|
|
var
|
|
DeclEl: TPasElement;
|
|
begin
|
|
Result:=nil;
|
|
while El<>nil do
|
|
begin
|
|
if El.ReadAccessor<>nil then
|
|
begin
|
|
DeclEl:=(El.ReadAccessor.CustomData as TResolvedReference).Declaration;
|
|
Result:=DeclEl;
|
|
exit;
|
|
end;
|
|
El:=GetPasPropertyAncestor(El);
|
|
end;
|
|
end;
|
|
|
|
function TPasResolver.GetPasPropertySetter(El: TPasProperty): TPasElement;
|
|
// search the member variable or setter procedure of a property
|
|
var
|
|
DeclEl: TPasElement;
|
|
begin
|
|
Result:=nil;
|
|
while El<>nil do
|
|
begin
|
|
if El.WriteAccessor<>nil then
|
|
begin
|
|
DeclEl:=(El.WriteAccessor.CustomData as TResolvedReference).Declaration;
|
|
Result:=DeclEl;
|
|
exit;
|
|
end;
|
|
El:=GetPasPropertyAncestor(El);
|
|
end;
|
|
end;
|
|
|
|
function TPasResolver.GetPasPropertyStored(El: TPasProperty): TPasElement;
|
|
// search the member variable or setter procedure of a property
|
|
var
|
|
DeclEl: TPasElement;
|
|
begin
|
|
Result:=nil;
|
|
while El<>nil do
|
|
begin
|
|
if El.StoredAccessor<>nil then
|
|
begin
|
|
DeclEl:=(El.StoredAccessor.CustomData as TResolvedReference).Declaration;
|
|
Result:=DeclEl;
|
|
exit;
|
|
end;
|
|
El:=GetPasPropertyAncestor(El);
|
|
end;
|
|
end;
|
|
|
|
function TPasResolver.CheckParamCompatibility(Expr: TPasExpr;
|
|
Param: TPasArgument; ParamNo: integer; RaiseOnError: boolean): integer;
|
|
var
|
|
ExprResolved, ParamResolved: TPasResolverResult;
|
|
NeedVar: Boolean;
|
|
RHSFlags: TPasResolverComputeFlags;
|
|
begin
|
|
Result:=cIncompatible;
|
|
|
|
NeedVar:=Param.Access in [argVar, argOut];
|
|
|
|
ComputeElement(Param,ParamResolved,[]);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckParamCompatibility Param=',GetTreeDesc(Param,2),' ParamResolved=',GetResolverResultDesc(ParamResolved));
|
|
{$ENDIF}
|
|
if (ParamResolved.TypeEl=nil) and (Param.ArgType<>nil) then
|
|
RaiseInternalError(20160922163628,'GetResolvedType returned TypeEl=nil for '+GetTreeDesc(Param));
|
|
RHSFlags:=[];
|
|
if NeedVar then
|
|
Include(RHSFlags,rcNoImplicitProc)
|
|
else if IsProcedureType(ParamResolved,true) then
|
|
Include(RHSFlags,rcNoImplicitProcType);
|
|
|
|
if (Expr is TParamsExpr) and (TParamsExpr(Expr).Kind=pekSet) then
|
|
begin
|
|
// passing a const set
|
|
if NeedVar then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170216152450,nVariableIdentifierExpected,sVariableIdentifierExpected,[],Expr);
|
|
exit;
|
|
end;
|
|
if ParamResolved.TypeEl is TPasArrayType then
|
|
begin
|
|
Result:=CheckConstArrayCompatibility(TParamsExpr(Expr),ParamResolved,
|
|
RaiseOnError,RHSFlags,Expr);
|
|
if (Result=cIncompatible) and RaiseOnError then
|
|
RaiseInternalError(20170326211129);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
ComputeElement(Expr,ExprResolved,RHSFlags);
|
|
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckParamCompatibility Expr=',GetTreeDesc(Expr,2),' ResolvedExpr=',GetResolverResultDesc(ExprResolved),' RHSFlags=',dbgs(RHSFlags));
|
|
{$ENDIF}
|
|
|
|
if NeedVar then
|
|
begin
|
|
// Expr must be a variable
|
|
if not ResolvedElCanBeVarParam(ExprResolved) then
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckParamCompatibility NeedWritable: ',GetResolverResultDbg(ExprResolved));
|
|
{$ENDIF}
|
|
if RaiseOnError then
|
|
RaiseMsg(20170216152450,nVariableIdentifierExpected,sVariableIdentifierExpected,[],Expr);
|
|
exit;
|
|
end;
|
|
if (ParamResolved.BaseType=ExprResolved.BaseType) then
|
|
begin
|
|
if IsSameType(ParamResolved.TypeEl,ExprResolved.TypeEl) then
|
|
exit(cExact);
|
|
end;
|
|
if (Param.ArgType=nil) then
|
|
exit(cExact); // untyped argument
|
|
if RaiseOnError then
|
|
RaiseIncompatibleType(20170216152452,nIncompatibleTypeArgNoVarParamMustMatchExactly,
|
|
[IntToStr(ParamNo+1)],ExprResolved.TypeEl,ParamResolved.TypeEl,
|
|
Expr);
|
|
exit(cIncompatible);
|
|
end;
|
|
|
|
Result:=CheckAssignResCompatibility(ParamResolved,ExprResolved,Expr,false);
|
|
if (Result=cIncompatible) and RaiseOnError then
|
|
RaiseIncompatibleTypeRes(20170216152454,nIncompatibleTypeArgNo,
|
|
[IntToStr(ParamNo+1)],ExprResolved,ParamResolved,Expr);
|
|
end;
|
|
|
|
function TPasResolver.CheckAssignCompatibilityUserType(const LHS,
|
|
RHS: TPasResolverResult; ErrorEl: TPasElement; RaiseOnIncompatible: boolean
|
|
): integer;
|
|
var
|
|
RTypeEl, LTypeEl: TPasType;
|
|
SrcResolved, DstResolved: TPasResolverResult;
|
|
LArray, RArray: TPasArrayType;
|
|
|
|
function RaiseIncompatType: integer;
|
|
begin
|
|
if not RaiseOnIncompatible then exit(cIncompatible);
|
|
RaiseIncompatibleTypeRes(20170216152505,nIncompatibleTypesGotExpected,
|
|
[],RHS,LHS,ErrorEl);
|
|
end;
|
|
|
|
begin
|
|
if (RHS.TypeEl=nil) then
|
|
RaiseInternalError(20160922163645);
|
|
if (LHS.TypeEl=nil) then
|
|
RaiseInternalError(20160922163648);
|
|
LTypeEl:=LHS.TypeEl;
|
|
RTypeEl:=RHS.TypeEl;
|
|
if LTypeEl=RTypeEl then
|
|
exit(cExact);
|
|
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckAssignCompatibilityUserType LTypeEl=',GetObjName(LTypeEl),' RTypeEl=',GetObjName(RTypeEl));
|
|
{$ENDIF}
|
|
Result:=-1;
|
|
if LTypeEl.ClassType=TPasClassType then
|
|
begin
|
|
if RHS.BaseType=btNil then
|
|
Result:=cExact
|
|
else if RTypeEl.ClassType=TPasClassType then
|
|
begin
|
|
Result:=CheckSrcIsADstType(RHS,LHS,ErrorEl);
|
|
if (Result=cIncompatible) and RaiseOnIncompatible then
|
|
RaiseIncompatibleType(20170216152458,nIncompatibleTypesGotExpected,
|
|
[],RTypeEl,LTypeEl,ErrorEl);
|
|
end
|
|
else
|
|
exit(RaiseIncompatType);
|
|
end
|
|
else if LTypeEl.ClassType=TPasClassOfType then
|
|
begin
|
|
if RHS.BaseType=btNil then
|
|
Result:=cExact
|
|
else if (RTypeEl.ClassType=TPasClassOfType) then
|
|
begin
|
|
// e.g. ImageClass:=AnotherImageClass;
|
|
Result:=CheckClassIsClass(TPasClassOfType(RTypeEl).DestType,
|
|
TPasClassOfType(LTypeEl).DestType,ErrorEl);
|
|
if (Result=cIncompatible) and RaiseOnIncompatible then
|
|
RaiseMsg(20170216152500,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected,
|
|
['class of '+TPasClassOfType(RTypeEl).DestType.FullName,'class of '+TPasClassOfType(LTypeEl).DestType.FullName],ErrorEl);
|
|
end
|
|
else if (RHS.IdentEl is TPasClassType) then
|
|
begin
|
|
// e.g. ImageClass:=TFPMemoryImage;
|
|
Result:=CheckClassIsClass(RTypeEl,TPasClassOfType(LTypeEl).DestType,ErrorEl);
|
|
if (Result=cIncompatible) and RaiseOnIncompatible then
|
|
RaiseMsg(20170216152501,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected,
|
|
[RTypeEl.Name,'class of '+TPasClassOfType(LTypeEl).DestType.FullName],ErrorEl);
|
|
// do not check rrfReadable -> exit
|
|
exit;
|
|
end;
|
|
end
|
|
else if LTypeEl is TPasProcedureType then
|
|
begin
|
|
if RHS.BaseType=btNil then
|
|
exit(cExact);
|
|
//writeln('TPasResolver.CheckAssignCompatibilityUserType LTypeEl=',GetObjName(LTypeEl),' RHS.BaseType=',BaseTypeNames[RHS.BaseType],' RTypeEl=',GetObjName(RTypeEl),' RHS.IdentEl=',GetObjName(RHS.IdentEl),' RHS.ExprEl=',GetObjName(RHS.ExprEl),' rrfReadable=',rrfReadable in RHS.Flags);
|
|
if (LTypeEl.ClassType=RTypeEl.ClassType)
|
|
and (rrfReadable in RHS.Flags) then
|
|
begin
|
|
// e.g. ProcVar1:=ProcVar2
|
|
if CheckProcTypeCompatibility(TPasProcedureType(LTypeEl),TPasProcedureType(RTypeEl),
|
|
ErrorEl,RaiseOnIncompatible) then
|
|
exit(cExact);
|
|
end;
|
|
if RaiseOnIncompatible then
|
|
begin
|
|
if (RTypeEl is TPasProcedureType) and (rrfReadable in RHS.Flags) then
|
|
RaiseMsg(20170404154738,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected,
|
|
[RTypeEl.ElementTypeName,LTypeEl.ElementTypeName],ErrorEl);
|
|
end;
|
|
end
|
|
else if LTypeEl.ClassType=TPasArrayType then
|
|
begin
|
|
// arrays of different types
|
|
if IsOpenArray(LTypeEl) and (RTypeEl.ClassType=TPasArrayType) then
|
|
begin
|
|
LArray:=TPasArrayType(LTypeEl);
|
|
RArray:=TPasArrayType(RTypeEl);
|
|
if length(LArray.Ranges)=length(RArray.Ranges) then
|
|
begin
|
|
if CheckProcArgTypeCompatibility(LArray.ElType,RArray.ElType) then
|
|
Result:=cExact
|
|
else if RaiseOnIncompatible then
|
|
RaiseMsg(20170328110050,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected,
|
|
['array of '+LArray.ElType.FullName,
|
|
'array of '+RArray.ElType.FullName],ErrorEl)
|
|
else
|
|
exit(cIncompatible);
|
|
end;
|
|
end;
|
|
end
|
|
else if RTypeEl.ClassType=TPasEnumType then
|
|
begin
|
|
// enums of different type
|
|
end
|
|
else if RTypeEl.ClassType=TPasSetType then
|
|
begin
|
|
// sets of different type are compatible if enum types are compatible
|
|
if LTypeEl.ClassType=TPasSetType then
|
|
begin
|
|
ComputeElement(TPasSetType(LTypeEl).EnumType,DstResolved,[]);
|
|
ComputeElement(TPasSetType(RTypeEl).EnumType,SrcResolved,[]);
|
|
if (SrcResolved.TypeEl<>nil)
|
|
and (SrcResolved.TypeEl=DstResolved.TypeEl) then
|
|
Result:=cExact
|
|
else if (SrcResolved.TypeEl.CustomData is TResElDataBaseType)
|
|
and (DstResolved.TypeEl.CustomData is TResElDataBaseType)
|
|
and (CompareText(SrcResolved.TypeEl.Name,DstResolved.TypeEl.Name)=0) then
|
|
Result:=cExact
|
|
else if RaiseOnIncompatible then
|
|
RaiseIncompatibleTypeRes(20170216152510,nIncompatibleTypesGotExpected,
|
|
[],SrcResolved,DstResolved,ErrorEl)
|
|
else
|
|
exit(cIncompatible);
|
|
end
|
|
else
|
|
exit(RaiseIncompatType);
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20160922163654,ErrorEl);
|
|
|
|
if Result=-1 then
|
|
exit(RaiseIncompatType);
|
|
if not (rrfReadable in RHS.Flags) then
|
|
exit(RaiseIncompatType);
|
|
end;
|
|
|
|
function TPasResolver.CheckAssignCompatibilityArrayType(const LHS,
|
|
RHS: TPasResolverResult; ErrorEl: TPasElement; RaiseOnIncompatible: boolean
|
|
): integer;
|
|
|
|
procedure CheckRange(ArrType: TPasArrayType; RangeIndex: integer;
|
|
Values: TPasResolverResult; ErrorEl: TPasElement);
|
|
var
|
|
Range, Value: TPasExpr;
|
|
RangeResolved, ValueResolved, ElTypeResolved: TPasResolverResult;
|
|
i, Count: Integer;
|
|
IsLastRange: Boolean;
|
|
ArrayValues: TPasExprArray;
|
|
begin
|
|
Range:=ArrType.Ranges[RangeIndex];
|
|
ComputeElement(Range,RangeResolved,[rcConstant]);
|
|
Count:=GetRangeLength(RangeResolved);
|
|
if Count=0 then
|
|
RaiseNotYetImplemented(20170222232409,Values.ExprEl,'range '+GetResolverResultDesc(RangeResolved));
|
|
|
|
IsLastRange:=RangeIndex+1=length(ArrType.Ranges);
|
|
if IsLastRange then
|
|
begin
|
|
ComputeElement(ArrType.ElType,ElTypeResolved,[rcType]);
|
|
ElTypeResolved.IdentEl:=Range;
|
|
Include(ElTypeResolved.Flags,rrfWritable);
|
|
end
|
|
else
|
|
ElTypeResolved.BaseType:=btNone;
|
|
|
|
if Values.ExprEl.ClassType=TArrayValues then
|
|
begin
|
|
ArrayValues:=TArrayValues(Values.ExprEl).Values;
|
|
// check each value
|
|
for i:=0 to Count-1 do
|
|
begin
|
|
if i=length(ArrayValues) then
|
|
begin
|
|
// not enough values
|
|
if length(ArrayValues)>0 then
|
|
ErrorEl:=ArrayValues[length(ArrayValues)-1];
|
|
RaiseMsg(20170222233001,nExpectXArrayElementsButFoundY,sExpectXArrayElementsButFoundY,
|
|
[IntToStr(Count),IntToStr(length(ArrayValues))],ErrorEl);
|
|
end;
|
|
Value:=ArrayValues[i];
|
|
ComputeElement(Value,ValueResolved,[rcConstant]);
|
|
if IsLastRange then
|
|
begin
|
|
// last dimension -> check element type
|
|
Result:=CheckAssignResCompatibility(ElTypeResolved,ValueResolved,Value,RaiseOnIncompatible);
|
|
if Result=cIncompatible then
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
// multi dimensional array -> check next range
|
|
CheckRange(ArrType,RangeIndex+1,ValueResolved,Value);
|
|
end;
|
|
end;
|
|
if Count<length(ArrayValues) then
|
|
begin
|
|
// too many values
|
|
ErrorEl:=ArrayValues[Count];
|
|
RaiseMsg(20170222233605,nExpectXArrayElementsButFoundY,sExpectXArrayElementsButFoundY,
|
|
[IntToStr(Count),IntToStr(length(ArrayValues))],ErrorEl);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
// single value
|
|
// Note: the parser does not store the difference between (1) and 1
|
|
if (not IsLastRange) or (Count>1) then
|
|
RaiseMsg(20170223095307,nExpectXArrayElementsButFoundY,sExpectXArrayElementsButFoundY,
|
|
[IntToStr(Count),'1'],ErrorEl);
|
|
// check element type
|
|
Result:=CheckAssignResCompatibility(ElTypeResolved,Values,ErrorEl,RaiseOnIncompatible);
|
|
if Result=cIncompatible then
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
ArrType: TPasArrayType;
|
|
begin
|
|
Result:=cIncompatible;
|
|
if (LHS.BaseType<>btContext) or (not (LHS.TypeEl is TPasArrayType)) then
|
|
RaiseInternalError(20170222230012);
|
|
ArrType:=TPasArrayType(LHS.TypeEl);
|
|
if RHS.ExprEl=nil then
|
|
RaiseNotYetImplemented(20170222230246,ErrorEl);
|
|
CheckRange(ArrType,0,RHS,ErrorEl);
|
|
end;
|
|
|
|
function TPasResolver.CheckConstArrayCompatibility(Params: TParamsExpr;
|
|
const ArrayResolved: TPasResolverResult; RaiseOnError: boolean;
|
|
Flags: TPasResolverComputeFlags; StartEl: TPasElement): integer;
|
|
// check that each Param fits the array element type
|
|
var
|
|
i, ParamComp: Integer;
|
|
Param: TPasExpr;
|
|
ArrayType: TPasArrayType;
|
|
ElTypeResolved, ParamResolved: TPasResolverResult;
|
|
ElTypeIsArray: boolean;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckConstArrayCompatibility Params.length=',length(Params.Params),
|
|
' ArrayResolved=',GetResolverResultDesc(ArrayResolved),' Flags=',dbgs(Flags));
|
|
{$ENDIF}
|
|
if not (ArrayResolved.TypeEl is TPasArrayType) then
|
|
RaiseInternalError(20170326204957);
|
|
ArrayType:=TPasArrayType(ArrayResolved.TypeEl);
|
|
ComputeElement(ArrayType.ElType,ElTypeResolved,Flags+[rcType]);
|
|
ElTypeIsArray:=ResolveAliasType(ElTypeResolved.TypeEl) is TPasArrayType;
|
|
Result:=cExact;
|
|
for i:=0 to length(Params.Params)-1 do
|
|
begin
|
|
Param:=Params.Params[i];
|
|
if ElTypeIsArray and (Param is TParamsExpr) and (TParamsExpr(Param).Kind=pekSet) then
|
|
ParamComp:=CheckConstArrayCompatibility(TParamsExpr(Param),ElTypeResolved,
|
|
RaiseOnError,Flags,StartEl)
|
|
else
|
|
begin
|
|
ComputeElement(Param,ParamResolved,Flags,StartEl);
|
|
ParamComp:=CheckAssignResCompatibility(ElTypeResolved,ParamResolved,Param,RaiseOnError);
|
|
end;
|
|
if ParamComp=cIncompatible then
|
|
exit(cIncompatible);
|
|
inc(Result,ParamComp);
|
|
end;
|
|
end;
|
|
|
|
function TPasResolver.CheckEqualCompatibilityUserType(const TypeA,
|
|
TypeB: TPasResolverResult; ErrorEl: TPasElement; RaiseOnIncompatible: boolean
|
|
): integer;
|
|
var
|
|
ElA, ElB: TPasType;
|
|
AResolved, BResolved: TPasResolverResult;
|
|
|
|
function IncompatibleElements: integer;
|
|
begin
|
|
Result:=cIncompatible;
|
|
if not RaiseOnIncompatible then exit;
|
|
RaiseIncompatibleType(20170216152513,nIncompatibleTypesGotExpected,
|
|
[],ElA,ElB,ErrorEl);
|
|
end;
|
|
|
|
begin
|
|
if (TypeA.TypeEl=nil) then
|
|
RaiseInternalError(20161007223118);
|
|
if (TypeB.TypeEl=nil) then
|
|
RaiseInternalError(20161007223119);
|
|
ElA:=TypeA.TypeEl;
|
|
ElB:=TypeB.TypeEl;
|
|
if ElA=ElB then
|
|
exit(cExact);
|
|
|
|
if ElA.ClassType=TPasClassType then
|
|
begin
|
|
if TypeA.IdentEl is TPasType then
|
|
begin
|
|
if (TypeB.IdentEl is TPasType) and (ElA=ElB) then
|
|
// e.g. if TFPMemoryImage=TFPMemoryImage then ;
|
|
exit(cExact);
|
|
if ElB.ClassType=TPasClassOfType then
|
|
begin
|
|
// e.g. if TFPMemoryImage=ImageClass then ;
|
|
Result:=CheckClassIsClass(ElA,TPasClassOfType(ElB).DestType,ErrorEl);
|
|
if (Result=cIncompatible) and RaiseOnIncompatible then
|
|
RaiseMsg(20170216152515,nTypesAreNotRelated,sTypesAreNotRelated,[],ErrorEl);
|
|
exit;
|
|
end;
|
|
end
|
|
else if ElB.ClassType=TPasClassType then
|
|
begin
|
|
// e.g. if Sender=Button1 then
|
|
Result:=CheckSrcIsADstType(TypeA,TypeB,ErrorEl);
|
|
if Result=cIncompatible then
|
|
Result:=CheckSrcIsADstType(TypeB,TypeA,ErrorEl);
|
|
if (Result=cIncompatible) and RaiseOnIncompatible then
|
|
RaiseMsg(20170216152517,nTypesAreNotRelated,sTypesAreNotRelated,[],ErrorEl);
|
|
exit;
|
|
end;
|
|
exit(IncompatibleElements);
|
|
end
|
|
else if ElA.ClassType=TPasClassOfType then
|
|
begin
|
|
if ElB.ClassType=TPasClassOfType then
|
|
begin
|
|
// for example: if ImageClass=ImageClass then
|
|
Result:=CheckClassIsClass(TPasClassOfType(ElA).DestType,
|
|
TPasClassOfType(ElB).DestType,ErrorEl);
|
|
if Result=cIncompatible then
|
|
Result:=CheckClassIsClass(TPasClassOfType(ElB).DestType,
|
|
TPasClassOfType(ElA).DestType,ErrorEl);
|
|
if (Result=cIncompatible) and RaiseOnIncompatible then
|
|
RaiseMsg(20170216152519,nTypesAreNotRelated,sTypesAreNotRelated,[],ErrorEl);
|
|
exit;
|
|
end
|
|
else if TypeB.IdentEl is TPasClassType then
|
|
begin
|
|
// for example: if ImageClass=TFPMemoryImage then
|
|
Result:=CheckClassIsClass(TPasClassType(TypeB.IdentEl),TPasClassOfType(ElA).DestType,ErrorEl);
|
|
if (Result=cIncompatible) and RaiseOnIncompatible then
|
|
RaiseMsg(20170216152520,nTypesAreNotRelated,sTypesAreNotRelated,[],ErrorEl);
|
|
exit;
|
|
end;
|
|
exit(IncompatibleElements);
|
|
end
|
|
else if ElA.ClassType=TPasEnumType then
|
|
begin
|
|
// enums of different type
|
|
if not RaiseOnIncompatible then
|
|
exit(cIncompatible);
|
|
if ElB.ClassType=TPasEnumValue then
|
|
RaiseIncompatibleType(20170216152523,nIncompatibleTypesGotExpected,
|
|
[],TPasEnumType(ElA),TPasEnumType(ElB),ErrorEl)
|
|
else
|
|
exit(IncompatibleElements);
|
|
end
|
|
else if ElA.ClassType=TPasSetType then
|
|
begin
|
|
if ElB.ClassType=TPasSetType then
|
|
begin
|
|
ComputeElement(TPasSetType(ElA).EnumType,AResolved,[]);
|
|
ComputeElement(TPasSetType(ElB).EnumType,BResolved,[]);
|
|
if (AResolved.TypeEl<>nil)
|
|
and (AResolved.TypeEl=BResolved.TypeEl) then
|
|
exit(cExact);
|
|
if (AResolved.TypeEl.CustomData is TResElDataBaseType)
|
|
and (BResolved.TypeEl.CustomData is TResElDataBaseType)
|
|
and (CompareText(AResolved.TypeEl.Name,BResolved.TypeEl.Name)=0) then
|
|
exit(cExact);
|
|
if RaiseOnIncompatible then
|
|
RaiseIncompatibleTypeRes(20170216152524,nIncompatibleTypesGotExpected,
|
|
[],AResolved,BResolved,ErrorEl)
|
|
else
|
|
exit(cIncompatible);
|
|
end
|
|
else
|
|
exit(IncompatibleElements);
|
|
end
|
|
else if (ElA is TPasProcedureType) and (rrfReadable in TypeA.Flags) then
|
|
begin
|
|
if (ElB is TPasProcedureType) and (rrfReadable in TypeB.Flags) then
|
|
begin
|
|
// e.g. ProcVar1 = ProcVar2
|
|
if CheckProcTypeCompatibility(TPasProcedureType(ElA),TPasProcedureType(ElB),
|
|
nil,false) then
|
|
exit(cExact);
|
|
end
|
|
else
|
|
exit(IncompatibleElements);
|
|
end;
|
|
exit(IncompatibleElements);
|
|
end;
|
|
|
|
function TPasResolver.CheckTypeCast(El: TPasType; Params: TParamsExpr;
|
|
RaiseOnError: boolean): integer;
|
|
// for example if TClassA(AnObject)=nil then ;
|
|
var
|
|
Param: TPasExpr;
|
|
ParamResolved, ResolvedEl: TPasResolverResult;
|
|
begin
|
|
if length(Params.Params)<>1 then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170216152526,nWrongNumberOfParametersForTypeCast,
|
|
sWrongNumberOfParametersForTypeCast,[El.Name],Params);
|
|
exit(cIncompatible);
|
|
end;
|
|
Param:=Params.Params[0];
|
|
ComputeElement(Param,ParamResolved,[rcNoImplicitProcType]);
|
|
ComputeElement(El,ResolvedEl,[rcType]);
|
|
Result:=CheckTypeCastRes(ParamResolved,ResolvedEl,Param,RaiseOnError);
|
|
end;
|
|
|
|
function TPasResolver.CheckTypeCastRes(const FromResolved,
|
|
ToResolved: TPasResolverResult; ErrorEl: TPasElement; RaiseOnError: boolean
|
|
): integer;
|
|
var
|
|
ToTypeEl, ToClassType, FromClassType: TPasType;
|
|
ToTypeBaseType: TResolverBaseType;
|
|
C: TClass;
|
|
ToProcType, FromProcType: TPasProcedureType;
|
|
begin
|
|
Result:=cIncompatible;
|
|
ToTypeEl:=ResolveAliasType(ToResolved.TypeEl);
|
|
if (ToTypeEl<>nil)
|
|
and (rrfReadable in FromResolved.Flags) then
|
|
begin
|
|
C:=ToTypeEl.ClassType;
|
|
if FromResolved.BaseType=btUntyped then
|
|
begin
|
|
// typecast an untyped parameter
|
|
Result:=cExact+1;
|
|
end
|
|
else if C=TPasUnresolvedSymbolRef then
|
|
begin
|
|
if ToTypeEl.CustomData is TResElDataBaseType then
|
|
begin
|
|
// base type cast, e.g. double(aninteger)
|
|
if ToTypeEl=FromResolved.TypeEl then
|
|
exit(cExact);
|
|
ToTypeBaseType:=(ToTypeEl.CustomData as TResElDataBaseType).BaseType;
|
|
if ToTypeBaseType=FromResolved.BaseType then
|
|
Result:=cExact
|
|
else if ToTypeBaseType in btAllInteger then
|
|
begin
|
|
if FromResolved.BaseType in (btAllInteger+btAllBooleans) then
|
|
Result:=cExact+1;
|
|
end
|
|
else if ToTypeBaseType in btAllFloats then
|
|
begin
|
|
if FromResolved.BaseType in (btAllInteger+btAllFloats) then
|
|
Result:=cExact+1;
|
|
end
|
|
else if ToTypeBaseType in btAllBooleans then
|
|
begin
|
|
if FromResolved.BaseType in (btAllBooleans+btAllInteger) then
|
|
Result:=cExact+1;
|
|
end
|
|
else if ToTypeBaseType in btAllStrings then
|
|
begin
|
|
if FromResolved.BaseType in btAllStringAndChars then
|
|
Result:=cExact+1;
|
|
end
|
|
else if ToTypeBaseType=btPointer then
|
|
begin
|
|
if FromResolved.BaseType=btPointer then
|
|
Result:=cExact
|
|
else if FromResolved.BaseType=btContext then
|
|
begin
|
|
C:=FromResolved.TypeEl.ClassType;
|
|
if (C=TPasClassType)
|
|
or (C=TPasClassOfType)
|
|
or (C=TPasPointerType)
|
|
or ((C=TPasArrayType) and IsDynArray(FromResolved.TypeEl)) then
|
|
Result:=cExact
|
|
else if (C=TPasProcedureType) or (C=TPasFunctionType) then
|
|
begin
|
|
// from procvar to pointer
|
|
FromProcType:=TPasProcedureType(FromResolved.TypeEl);
|
|
if FromProcType.IsOfObject then
|
|
begin
|
|
if proMethodAddrAsPointer in Options then
|
|
Result:=cExact+1
|
|
else if RaiseOnError then
|
|
RaiseMsg(20170416183615,nIllegalTypeConversionTo,sIllegalTypeConversionTo,
|
|
[FromProcType.ElementTypeName+' '+ProcTypeModifiers[ptmOfObject],
|
|
BaseTypeNames[btPointer]],ErrorEl);
|
|
end
|
|
else if FromProcType.IsNested then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170416183800,nIllegalTypeConversionTo,sIllegalTypeConversionTo,
|
|
[FromProcType.ElementTypeName+' '+ProcTypeModifiers[ptmIsNested],
|
|
BaseTypeNames[btPointer]],ErrorEl);
|
|
end
|
|
else
|
|
Result:=cExact+1;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end
|
|
else if C=TPasClassType then
|
|
begin
|
|
// to class
|
|
if FromResolved.BaseType=btContext then
|
|
begin
|
|
if FromResolved.TypeEl.ClassType=TPasClassType then
|
|
begin
|
|
if FromResolved.IdentEl is TPasType then
|
|
RaiseMsg(20170404162606,nCannotTypecastAType,sCannotTypecastAType,[],ErrorEl);
|
|
// type cast upwards or downwards
|
|
Result:=CheckSrcIsADstType(FromResolved,ToResolved,ErrorEl);
|
|
if Result=cIncompatible then
|
|
Result:=CheckSrcIsADstType(ToResolved,FromResolved,ErrorEl);
|
|
if Result=cIncompatible then
|
|
Result:=CheckTypeCastClassInstanceToClass(FromResolved,ToResolved,ErrorEl);
|
|
end
|
|
end
|
|
else if FromResolved.BaseType=btPointer then
|
|
begin
|
|
if IsBaseType(FromResolved.TypeEl,btPointer) then
|
|
Result:=cExact; // untyped pointer to class instance
|
|
end;
|
|
end
|
|
else if C=TPasClassOfType then
|
|
begin
|
|
//writeln('TPasResolver.CheckTypeCast class-of FromRes.TypeEl=',GetObjName(FromResolved.TypeEl),' FromRes.IdentEl=',GetObjName(FromResolved.IdentEl));
|
|
if FromResolved.BaseType=btContext then
|
|
begin
|
|
if FromResolved.TypeEl.ClassType=TPasClassOfType then
|
|
begin
|
|
if (FromResolved.IdentEl is TPasType) then
|
|
RaiseMsg(20170404162604,nCannotTypecastAType,sCannotTypecastAType,[],ErrorEl);
|
|
// type cast classof(classof-var) upwards or downwards
|
|
ToClassType:=TPasClassOfType(ToTypeEl).DestType;
|
|
FromClassType:=TPasClassOfType(FromResolved.TypeEl).DestType;
|
|
Result:=CheckClassesAreRelated(ToClassType,FromClassType,ErrorEl);
|
|
end;
|
|
end
|
|
else if FromResolved.BaseType=btPointer then
|
|
begin
|
|
if IsBaseType(FromResolved.TypeEl,btPointer) then
|
|
Result:=cExact; // untyped pointer to class-of
|
|
end;
|
|
end
|
|
else if C=TPasEnumType then
|
|
begin
|
|
if CheckIsOrdinal(FromResolved,ErrorEl,true) then
|
|
Result:=cExact;
|
|
end
|
|
else if C=TPasArrayType then
|
|
begin
|
|
if FromResolved.BaseType=btContext then
|
|
begin
|
|
if FromResolved.TypeEl.ClassType=TPasArrayType then
|
|
Result:=CheckTypeCastArray(TPasArrayType(FromResolved.TypeEl),
|
|
TPasArrayType(ToTypeEl),ErrorEl,RaiseOnError);
|
|
end
|
|
else if FromResolved.BaseType=btPointer then
|
|
begin
|
|
if IsDynArray(ToResolved.TypeEl)
|
|
and IsBaseType(FromResolved.TypeEl,btPointer) then
|
|
Result:=cExact; // untyped pointer to dynnamic array
|
|
end;
|
|
end
|
|
else if (C=TPasProcedureType) or (C=TPasFunctionType) then
|
|
begin
|
|
ToProcType:=TPasProcedureType(ToTypeEl);
|
|
if IsBaseType(FromResolved.TypeEl,btPointer) then
|
|
begin
|
|
// type cast untyped pointer value to proctype
|
|
if ToProcType.IsOfObject then
|
|
begin
|
|
if proMethodAddrAsPointer in Options then
|
|
Result:=cExact+1
|
|
else if RaiseOnError then
|
|
RaiseMsg(20170416183940,nIllegalTypeConversionTo,sIllegalTypeConversionTo,
|
|
[BaseTypeNames[btPointer],
|
|
ToProcType.ElementTypeName+' '+ProcTypeModifiers[ptmOfObject]],ErrorEl);
|
|
end
|
|
else if ToProcType.IsNested then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170416184149,nIllegalTypeConversionTo,sIllegalTypeConversionTo,
|
|
[BaseTypeNames[btPointer],
|
|
ToProcType.ElementTypeName+' '+ProcTypeModifiers[ptmIsNested]],ErrorEl);
|
|
end
|
|
else
|
|
Result:=cExact+1;
|
|
end
|
|
else if FromResolved.BaseType=btContext then
|
|
begin
|
|
if FromResolved.TypeEl is TPasProcedureType then
|
|
begin
|
|
// type cast procvar to proctype
|
|
FromProcType:=TPasProcedureType(FromResolved.TypeEl);
|
|
if (FromProcType.IsOfObject<>ToProcType.IsOfObject)
|
|
and not (proMethodAddrAsPointer in Options) then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170416183109,nIllegalTypeConversionTo,sIllegalTypeConversionTo,
|
|
[FromProcType.ElementTypeName+BoolToStr(FromProcType.IsOfObject,' '+ProcTypeModifiers[ptmOfObject],''),
|
|
ToProcType.ElementTypeName+BoolToStr(ToProcType.IsOfObject,' '+ProcTypeModifiers[ptmOfObject],'')],ErrorEl);
|
|
end
|
|
else if FromProcType.IsNested<>ToProcType.IsNested then
|
|
begin
|
|
if RaiseOnError then
|
|
RaiseMsg(20170416183305,nIllegalTypeConversionTo,sIllegalTypeConversionTo,
|
|
[FromProcType.ElementTypeName+BoolToStr(FromProcType.IsNested,' '+ProcTypeModifiers[ptmIsNested],''),
|
|
ToProcType.ElementTypeName+BoolToStr(ToProcType.IsNested,' '+ProcTypeModifiers[ptmIsNested],'')],ErrorEl);
|
|
end
|
|
else
|
|
Result:=cExact+1;
|
|
end;
|
|
end;
|
|
end;
|
|
end
|
|
else if ToTypeEl<>nil then
|
|
begin
|
|
// FromResolved is not readable
|
|
if FromResolved.BaseType=btContext then
|
|
begin
|
|
if (FromResolved.TypeEl.ClassType=TPasClassType)
|
|
and (FromResolved.TypeEl=FromResolved.IdentEl)
|
|
and (ToResolved.BaseType=btContext)
|
|
and (ToResolved.TypeEl.ClassType=TPasClassOfType)
|
|
and (ToResolved.TypeEl=ToResolved.IdentEl) then
|
|
begin
|
|
// for example class-of(Self) in a class function
|
|
ToClassType:=TPasClassOfType(ToTypeEl).DestType;
|
|
FromClassType:=TPasClassType(FromResolved.TypeEl);
|
|
Result:=CheckClassesAreRelated(ToClassType,FromClassType,ErrorEl);
|
|
end;
|
|
end;
|
|
if (Result=cIncompatible) and RaiseOnError then
|
|
begin
|
|
if FromResolved.IdentEl is TPasType then
|
|
RaiseMsg(20170404162610,nCannotTypecastAType,sCannotTypecastAType,[],ErrorEl);
|
|
end;
|
|
end;
|
|
|
|
if Result=cIncompatible then
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckTypeCastRes From={',GetResolverResultDbg(FromResolved),'} To={',GetResolverResultDbg(ToResolved),'}');
|
|
{$ENDIF}
|
|
if RaiseOnError then
|
|
RaiseIncompatibleTypeRes(20170216152528,nIllegalTypeConversionTo,
|
|
[],FromResolved,ToResolved,ErrorEl);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
function TPasResolver.CheckTypeCastArray(FromType, ToType: TPasArrayType;
|
|
ErrorEl: TPasElement; RaiseOnError: boolean): integer;
|
|
|
|
function NextDim(var ArrType: TPasArrayType; var NextIndex: integer;
|
|
out ElTypeResolved: TPasResolverResult): boolean;
|
|
begin
|
|
inc(NextIndex);
|
|
if NextIndex<length(ArrType.Ranges) then
|
|
begin
|
|
ElTypeResolved.BaseType:=btNone;
|
|
exit(true);
|
|
end;
|
|
ComputeElement(ArrType.ElType,ElTypeResolved,[rcType]);
|
|
if (ElTypeResolved.BaseType<>btContext)
|
|
or (ElTypeResolved.TypeEl.ClassType<>TPasArrayType) then
|
|
exit(false);
|
|
ArrType:=TPasArrayType(ElTypeResolved.TypeEl);
|
|
NextIndex:=0;
|
|
Result:=true;
|
|
end;
|
|
|
|
var
|
|
FromIndex, ToIndex: Integer;
|
|
FromElTypeRes, ToElTypeRes: TPasResolverResult;
|
|
StartFromType, StartToType: TPasArrayType;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckTypeCastArray From=',GetTypeDesc(FromType),' ToType=',GetTypeDesc(ToType));
|
|
{$ENDIF}
|
|
StartFromType:=FromType;
|
|
StartToType:=ToType;
|
|
Result:=cIncompatible;
|
|
// check dimensions
|
|
FromIndex:=0;
|
|
ToIndex:=0;
|
|
repeat
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckTypeCastArray From=',GetTypeDesc(FromType),' FromIndex=',FromIndex,' ToType=',GetTypeDesc(ToType),' ToIndex=',ToIndex);
|
|
{$ENDIF}
|
|
if length(ToType.Ranges)=0 then
|
|
// ToType is dynamic/open array -> fits any size
|
|
else
|
|
begin
|
|
// ToType is ranged
|
|
// ToDo: check size of dimension
|
|
end;
|
|
// check next dimension
|
|
if not NextDim(FromType,FromIndex,FromElTypeRes) then
|
|
begin
|
|
// at end of FromType
|
|
if NextDim(ToType,ToIndex,ToElTypeRes) then
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckTypeCastArray To has more dims than From: From=',GetTypeDesc(FromType),' FromIndex=',FromIndex,', ToType=',GetTypeDesc(ToType),' ToIndex=',ToIndex);
|
|
{$ENDIF}
|
|
break; // ToType has more dimensions
|
|
end;
|
|
// have same dimension -> check ElType
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckTypeCastArray check ElType From=',GetResolverResultDesc(FromElTypeRes),' To=',GetResolverResultDesc(ToElTypeRes));
|
|
{$ENDIF}
|
|
Include(FromElTypeRes.Flags,rrfReadable);
|
|
Result:=CheckTypeCastRes(FromElTypeRes,ToElTypeRes,ErrorEl,false);
|
|
break;
|
|
end
|
|
else
|
|
begin
|
|
// FromType has more dimensions
|
|
if not NextDim(ToType,ToIndex,ToElTypeRes) then
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckTypeCastArray From has more dims than To: From=',GetTypeDesc(FromType),' FromIndex=',FromIndex,', ToType=',GetTypeDesc(ToType),' ToIndex=',ToIndex);
|
|
{$ENDIF}
|
|
break; // ToType has less dimensions
|
|
end;
|
|
end;
|
|
until false;
|
|
if (Result=cIncompatible) and RaiseOnError then
|
|
RaiseIncompatibleType(20170331124643,nIllegalTypeConversionTo,
|
|
[],StartFromType,StartToType,ErrorEl);
|
|
end;
|
|
|
|
procedure TPasResolver.ComputeElement(El: TPasElement; out
|
|
ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
|
|
StartEl: TPasElement);
|
|
|
|
procedure ComputeIdentifier;
|
|
var
|
|
Ref: TResolvedReference;
|
|
Proc: TPasProcedure;
|
|
ProcType: TPasProcedureType;
|
|
aClass: TPasClassType;
|
|
begin
|
|
Ref:=TResolvedReference(El.CustomData);
|
|
ComputeElement(Ref.Declaration,ResolvedEl,Flags+[rcNoImplicitProc],StartEl);
|
|
if rrfConstInherited in Ref.Flags then
|
|
Exclude(ResolvedEl.Flags,rrfWritable);
|
|
{$IFDEF VerbosePasResolver}
|
|
if El is TPrimitiveExpr then
|
|
writeln('TPasResolver.ComputeElement.ComputeIdentifier TPrimitiveExpr "',TPrimitiveExpr(El).Value,'" ',GetResolverResultDesc(ResolvedEl),' Flags=',dbgs(Flags))
|
|
else
|
|
writeln('TPasResolver.ComputeElement.ComputeIdentifier "',GetObjName(El),'" ',GetResolverResultDesc(ResolvedEl),' Flags=',dbgs(Flags));
|
|
{$ENDIF}
|
|
if (ResolvedEl.BaseType=btProc) then
|
|
begin
|
|
if [rcNoImplicitProc,rcConstant,rcType]*Flags=[] then
|
|
begin
|
|
// a proc and implicit call without params is allowed -> check if possible
|
|
Proc:=ResolvedEl.IdentEl as TPasProcedure;
|
|
if not ProcNeedsParams(Proc.ProcType) then
|
|
begin
|
|
// parameter less proc -> implicit call
|
|
Include(Ref.Flags,rrfImplicitCallWithoutParams);
|
|
if ResolvedEl.IdentEl is TPasFunction then
|
|
// function => return result
|
|
ComputeElement(TPasFunction(ResolvedEl.IdentEl).FuncType.ResultEl,
|
|
ResolvedEl,Flags+[rcType],StartEl)
|
|
else if (ResolvedEl.IdentEl.ClassType=TPasConstructor)
|
|
and (rrfNewInstance in Ref.Flags) then
|
|
begin
|
|
// new instance constructor -> return value of type class
|
|
aClass:=GetReference_NewInstanceClass(Ref);
|
|
SetResolverValueExpr(ResolvedEl,btContext,aClass,TPrimitiveExpr(El),[rrfReadable]);
|
|
end;
|
|
Include(ResolvedEl.Flags,rrfCanBeStatement);
|
|
end;
|
|
end;
|
|
end
|
|
else if IsProcedureType(ResolvedEl,true) then
|
|
begin
|
|
if [rcNoImplicitProc,rcNoImplicitProcType,rcConstant,rcType]*Flags=[] then
|
|
begin
|
|
// a proc type and implicit call without params is allowed -> check if possible
|
|
ProcType:=TPasProcedureType(ResolvedEl.TypeEl);
|
|
if not ProcNeedsParams(ProcType) then
|
|
begin
|
|
// parameter less proc -> implicit call
|
|
Include(Ref.Flags,rrfImplicitCallWithoutParams);
|
|
if ResolvedEl.TypeEl is TPasFunctionType then
|
|
// function => return result
|
|
ComputeElement(TPasFunctionType(ResolvedEl.TypeEl).ResultEl,
|
|
ResolvedEl,Flags+[rcType],StartEl);
|
|
Include(ResolvedEl.Flags,rrfCanBeStatement);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
DeclEl: TPasElement;
|
|
ElClass: TClass;
|
|
begin
|
|
if StartEl=nil then StartEl:=El;
|
|
ResolvedEl:=Default(TPasResolverResult);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ComputeElement El=',GetObjName(El),' SkipTypeAlias=',rcSkipTypeAlias in Flags);
|
|
{$ENDIF}
|
|
if El=nil then
|
|
exit;
|
|
ElClass:=El.ClassType;
|
|
if ElClass=TPrimitiveExpr then
|
|
begin
|
|
case TPrimitiveExpr(El).Kind of
|
|
pekIdent,pekSelf:
|
|
begin
|
|
if not (El.CustomData is TResolvedReference) then
|
|
RaiseNotYetImplemented(20160922163658,El,'Value="'+TPrimitiveExpr(El).Value+'" CustomData='+GetObjName(El.CustomData)+' '+GetElementSourcePosStr(El));
|
|
ComputeIdentifier;
|
|
end;
|
|
pekNumber:
|
|
// ToDo: check if btByte, btSmallInt, btSingle, ...
|
|
if Pos('.',TPrimitiveExpr(El).Value)>0 then
|
|
SetResolverValueExpr(ResolvedEl,btDouble,FBaseTypes[btDouble],TPrimitiveExpr(El),[rrfReadable])
|
|
else
|
|
SetResolverValueExpr(ResolvedEl,btLongint,FBaseTypes[btLongint],TPrimitiveExpr(El),[rrfReadable]);
|
|
pekString:
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ComputeElement pekString Value="',TPrimitiveExpr(El).Value,'"');
|
|
{$ENDIF}
|
|
if IsCharLiteral(TPrimitiveExpr(El).Value) then
|
|
SetResolverValueExpr(ResolvedEl,btChar,FBaseTypes[btChar],TPrimitiveExpr(El),[rrfReadable])
|
|
else
|
|
SetResolverValueExpr(ResolvedEl,btString,FBaseTypes[btString],TPrimitiveExpr(El),[rrfReadable]);
|
|
end;
|
|
pekNil:
|
|
SetResolverValueExpr(ResolvedEl,btNil,FBaseTypes[btNil],TPrimitiveExpr(El),[rrfReadable]);
|
|
pekBoolConst:
|
|
SetResolverValueExpr(ResolvedEl,btBoolean,FBaseTypes[btBoolean],TPrimitiveExpr(El),[rrfReadable]);
|
|
else
|
|
RaiseNotYetImplemented(20160922163701,El);
|
|
end;
|
|
end
|
|
else if ElClass=TSelfExpr then
|
|
begin
|
|
// self is just an identifier
|
|
if not (El.CustomData is TResolvedReference) then
|
|
RaiseNotYetImplemented(20170216150017,El,' El="'+GetObjName(El)+'" CustomData='+GetObjName(El.CustomData)+' '+GetElementSourcePosStr(El));
|
|
ComputeIdentifier;
|
|
end
|
|
else if ElClass=TPasUnresolvedSymbolRef then
|
|
begin
|
|
// built-in type
|
|
if El.CustomData is TResElDataBaseType then
|
|
SetResolverIdentifier(ResolvedEl,TResElDataBaseType(El.CustomData).BaseType,
|
|
El,TPasUnresolvedSymbolRef(El),[])
|
|
else if El.CustomData is TResElDataBuiltInProc then
|
|
begin
|
|
SetResolverIdentifier(ResolvedEl,btBuiltInProc,El,TPasUnresolvedSymbolRef(El),[]);
|
|
if bipfCanBeStatement in TResElDataBuiltInProc(El.CustomData).Flags then
|
|
Include(ResolvedEl.Flags,rrfCanBeStatement);
|
|
end
|
|
else
|
|
RaiseNotYetImplemented(20160926194756,El);
|
|
end
|
|
else if ElClass=TBoolConstExpr then
|
|
SetResolverValueExpr(ResolvedEl,btBoolean,FBaseTypes[btBoolean],TBoolConstExpr(El),[rrfReadable])
|
|
else if ElClass=TBinaryExpr then
|
|
ComputeBinaryExpr(TBinaryExpr(El),ResolvedEl,Flags,StartEl)
|
|
else if ElClass=TUnaryExpr then
|
|
begin
|
|
if TUnaryExpr(El).OpCode=eopAddress then
|
|
ComputeElement(TUnaryExpr(El).Operand,ResolvedEl,Flags+[rcNoImplicitProc],StartEl)
|
|
else
|
|
ComputeElement(TUnaryExpr(El).Operand,ResolvedEl,Flags,StartEl);
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.ComputeElement Unary Kind=',TUnaryExpr(El).Kind,' OpCode=',TUnaryExpr(El).OpCode,' OperandResolved=',GetResolverResultDesc(ResolvedEl),' ',GetElementSourcePosStr(El));
|
|
{$ENDIF}
|
|
case TUnaryExpr(El).OpCode of
|
|
eopAdd, eopSubtract:
|
|
if ResolvedEl.BaseType in (btAllInteger+btAllFloats) then
|
|
exit
|
|
else
|
|
RaiseMsg(20170216152532,nIllegalQualifier,sIllegalQualifier,[OpcodeStrings[TUnaryExpr(El).OpCode]],El);
|
|
eopNot:
|
|
if ResolvedEl.BaseType in (btAllInteger+btAllBooleans) then
|
|
exit
|
|
else
|
|
RaiseMsg(20170216152534,nIllegalQualifier,sIllegalQualifier,[OpcodeStrings[TUnaryExpr(El).OpCode]],El);
|
|
eopAddress:
|
|
if (ResolvedEl.BaseType=btProc) and (ResolvedEl.IdentEl is TPasProcedure) then
|
|
begin
|
|
SetResolverValueExpr(ResolvedEl,btContext,ResolvedEl.TypeEl,TUnaryExpr(El).Operand,[rrfReadable]);
|
|
exit;
|
|
end
|
|
else
|
|
RaiseMsg(20170216152535,nIllegalQualifier,sIllegalQualifier,[OpcodeStrings[TUnaryExpr(El).OpCode]],El);
|
|
end;
|
|
RaiseNotYetImplemented(20160926142426,El);
|
|
end
|
|
else if ElClass=TParamsExpr then
|
|
case TParamsExpr(El).Kind of
|
|
pekArrayParams:
|
|
ComputeArrayParams(TParamsExpr(El),ResolvedEl,Flags,StartEl);
|
|
pekFuncParams:
|
|
ComputeFuncParams(TParamsExpr(El),ResolvedEl,Flags,StartEl);
|
|
pekSet:
|
|
ComputeSetParams(TParamsExpr(El),ResolvedEl,Flags,StartEl);
|
|
else
|
|
RaiseNotYetImplemented(20161010184559,El);
|
|
end
|
|
else if ElClass=TInheritedExpr then
|
|
begin
|
|
// writeln('TPasResolver.ComputeElement TInheritedExpr El.CustomData=',GetObjName(El.CustomData));
|
|
if El.CustomData is TResolvedReference then
|
|
begin
|
|
// "inherited;"
|
|
DeclEl:=TResolvedReference(El.CustomData).Declaration as TPasProcedure;
|
|
SetResolverIdentifier(ResolvedEl,btProc,DeclEl,
|
|
TPasProcedure(DeclEl).ProcType,[rrfCanBeStatement]);
|
|
end
|
|
else
|
|
// no ancestor proc
|
|
SetResolverIdentifier(ResolvedEl,btBuiltInProc,nil,nil,[rrfCanBeStatement]);
|
|
end
|
|
else if ElClass=TPasAliasType then
|
|
begin
|
|
// e.g. 'type a = b' -> compute b
|
|
ComputeElement(TPasAliasType(El).DestType,ResolvedEl,Flags+[rcType],StartEl);
|
|
ResolvedEl.IdentEl:=El;
|
|
end
|
|
else if (ElClass=TPasTypeAliasType) then
|
|
begin
|
|
// e.g. 'type a = type b;' -> compute b
|
|
ComputeElement(TPasTypeAliasType(El).DestType,ResolvedEl,Flags+[rcType],StartEl);
|
|
if not (rcSkipTypeAlias in Flags) then
|
|
ResolvedEl.IdentEl:=El;
|
|
end
|
|
else if (ElClass=TPasVariable) then
|
|
begin
|
|
// e.g. 'var a:b' -> compute b, use a as IdentEl
|
|
if rcConstant in Flags then
|
|
RaiseConstantExprExp(20170216152737,StartEl);
|
|
ComputeElement(TPasVariable(El).VarType,ResolvedEl,Flags+[rcType],StartEl);
|
|
ResolvedEl.IdentEl:=El;
|
|
ResolvedEl.Flags:=[rrfReadable,rrfWritable];
|
|
end
|
|
else if (ElClass=TPasConst) then
|
|
begin
|
|
// e.g. 'var a:b' -> compute b, use a as IdentEl
|
|
if TPasConst(El).VarType<>nil then
|
|
begin
|
|
// typed const -> just like a var
|
|
if rcConstant in Flags then
|
|
RaiseConstantExprExp(20170216152739,StartEl);
|
|
ComputeElement(TPasConst(El).VarType,ResolvedEl,Flags+[rcType],StartEl);
|
|
ResolvedEl.IdentEl:=El;
|
|
ResolvedEl.Flags:=[rrfReadable,rrfWritable];
|
|
end
|
|
else
|
|
begin
|
|
// untyped const
|
|
ComputeElement(TPasConst(El).Expr,ResolvedEl,Flags+[rcConstant],StartEl);
|
|
ResolvedEl.IdentEl:=El;
|
|
ResolvedEl.Flags:=[rrfReadable];
|
|
end;
|
|
end
|
|
else if (ElClass=TPasEnumValue) then
|
|
SetResolverIdentifier(ResolvedEl,btContext,El,El.Parent as TPasEnumType,[rrfReadable])
|
|
else if (ElClass=TPasEnumType) then
|
|
SetResolverIdentifier(ResolvedEl,btContext,El,TPasEnumType(El),[rrfReadable])
|
|
else if (ElClass=TPasProperty) then
|
|
begin
|
|
if rcConstant in Flags then
|
|
RaiseConstantExprExp(20170216152741,StartEl);
|
|
if TPasProperty(El).Args.Count=0 then
|
|
begin
|
|
ComputeElement(GetPasPropertyType(TPasProperty(El)),ResolvedEl,
|
|
Flags+[rcType],StartEl);
|
|
ResolvedEl.IdentEl:=El;
|
|
ResolvedEl.Flags:=[];
|
|
if GetPasPropertyGetter(TPasProperty(El))<>nil then
|
|
Include(ResolvedEl.Flags,rrfReadable);
|
|
if GetPasPropertySetter(TPasProperty(El))<>nil then
|
|
Include(ResolvedEl.Flags,rrfWritable);
|
|
if IsProcedureType(ResolvedEl,true) then
|
|
Include(ResolvedEl.Flags,rrfCanBeStatement);
|
|
end
|
|
else
|
|
// index property
|
|
SetResolverIdentifier(ResolvedEl,btContext,El,nil,[]);
|
|
end
|
|
else if ElClass=TPasArgument then
|
|
begin
|
|
if rcConstant in Flags then
|
|
RaiseConstantExprExp(20170216152744,StartEl);
|
|
if TPasArgument(El).ArgType=nil then
|
|
// untyped parameter
|
|
SetResolverIdentifier(ResolvedEl,btUntyped,El,nil,[])
|
|
else
|
|
begin
|
|
// typed parameter -> use param as IdentEl, compute type
|
|
ComputeElement(TPasArgument(El).ArgType,ResolvedEl,Flags+[rcType],StartEl);
|
|
ResolvedEl.IdentEl:=El;
|
|
end;
|
|
ResolvedEl.Flags:=[rrfReadable];
|
|
if TPasArgument(El).Access in [argDefault, argVar, argOut] then
|
|
Include(ResolvedEl.Flags,rrfWritable);
|
|
if IsProcedureType(ResolvedEl,true) then
|
|
Include(ResolvedEl.Flags,rrfCanBeStatement);
|
|
end
|
|
else if ElClass=TPasClassType then
|
|
begin
|
|
if TPasClassType(El).IsForward then
|
|
begin
|
|
DeclEl:=(TPasClassType(El).CustomData as TResolvedReference).Declaration;
|
|
ResolvedEl.TypeEl:=DeclEl as TPasClassType;
|
|
end
|
|
else
|
|
ResolvedEl.TypeEl:=TPasClassType(El);
|
|
SetResolverIdentifier(ResolvedEl,btContext,
|
|
ResolvedEl.TypeEl,ResolvedEl.TypeEl,[]);
|
|
//if not TPasClassType(El).IsExternal then
|
|
// Include(ResolvedEl.Flags,rrfReadable);
|
|
// Note: rrfReadable because a class has a vmt as value
|
|
end
|
|
else if ElClass=TPasClassOfType then
|
|
SetResolverIdentifier(ResolvedEl,btContext,El,TPasClassOfType(El),[])
|
|
else if ElClass=TPasRecordType then
|
|
SetResolverIdentifier(ResolvedEl,btContext,El,TPasRecordType(El),[])
|
|
else if ElClass=TPasRangeType then
|
|
begin
|
|
ComputeElement(TPasRangeType(El).RangeExpr,ResolvedEl,[rcConstant],StartEl);
|
|
ResolvedEl.IdentEl:=El;
|
|
ResolvedEl.Flags:=[];
|
|
end
|
|
else if ElClass=TPasSetType then
|
|
begin
|
|
ComputeElement(TPasSetType(El).EnumType,ResolvedEl,[rcConstant],StartEl);
|
|
if ResolvedEl.BaseType=btRange then
|
|
ConvertRangeToFirstValue(ResolvedEl);
|
|
ResolvedEl.SubType:=ResolvedEl.BaseType;
|
|
ResolvedEl.BaseType:=btSet;
|
|
ResolvedEl.IdentEl:=El;
|
|
ResolvedEl.Flags:=[];
|
|
end
|
|
else if ElClass=TPasResultElement then
|
|
begin
|
|
if rcConstant in Flags then
|
|
RaiseConstantExprExp(20170216152746,StartEl);
|
|
ComputeElement(TPasResultElement(El).ResultType,ResolvedEl,Flags+[rcType],StartEl);
|
|
ResolvedEl.IdentEl:=El;
|
|
ResolvedEl.Flags:=[rrfReadable,rrfWritable];
|
|
end
|
|
else if El is TPasModule then
|
|
SetResolverIdentifier(ResolvedEl,btModule,El,nil,[])
|
|
else if ElClass=TNilExpr then
|
|
SetResolverValueExpr(ResolvedEl,btNil,FBaseTypes[btNil],TNilExpr(El),[rrfReadable])
|
|
else if El is TPasProcedure then
|
|
begin
|
|
SetResolverIdentifier(ResolvedEl,btProc,El,TPasProcedure(El).ProcType,[rrfCanBeStatement]);
|
|
if El is TPasFunction then
|
|
Include(ResolvedEl.Flags,rrfReadable);
|
|
// Note: the readability of TPasConstructor depends on the context
|
|
// Note: implicit calls are handled in TPrimitiveExpr
|
|
end
|
|
else if El is TPasProcedureType then
|
|
begin
|
|
SetResolverIdentifier(ResolvedEl,btContext,El,TPasProcedureType(El),[rrfCanBeStatement]);
|
|
// Note: implicit calls are handled in TPrimitiveExpr
|
|
end
|
|
else if ElClass=TPasArrayType then
|
|
SetResolverIdentifier(ResolvedEl,btContext,El,TPasArrayType(El),[])
|
|
else if ElClass=TArrayValues then
|
|
SetResolverValueExpr(ResolvedEl,btArray,nil,TArrayValues(El),[rrfReadable])
|
|
else
|
|
RaiseNotYetImplemented(20160922163705,El);
|
|
end;
|
|
|
|
function TPasResolver.IsSameType(TypeA, TypeB: TPasType): boolean;
|
|
begin
|
|
if TypeA=nil then exit(false);
|
|
if TypeA=TypeB then exit(true);
|
|
if (TypeA.ClassType=TPasUnresolvedSymbolRef)
|
|
and (TypeB.ClassType=TPasUnresolvedSymbolRef) then
|
|
begin
|
|
Result:=CompareText(TypeA.Name,TypeB.Name)=0;
|
|
exit;
|
|
end;
|
|
Result:=false;
|
|
end;
|
|
|
|
function TPasResolver.GetPasClassAncestor(ClassEl: TPasClassType;
|
|
SkipAlias: boolean): TPasType;
|
|
var
|
|
DeclEl: TPasElement;
|
|
ClassScope: TPasClassScope;
|
|
begin
|
|
Result:=nil;
|
|
if ClassEl=nil then
|
|
exit;
|
|
if ClassEl.CustomData=nil then
|
|
exit;
|
|
if ClassEl.IsForward then
|
|
begin
|
|
DeclEl:=(ClassEl.CustomData as TResolvedReference).Declaration;
|
|
ClassEl:=DeclEl as TPasClassType;
|
|
Result:=ClassEl;
|
|
end
|
|
else
|
|
begin
|
|
ClassScope:=ClassEl.CustomData as TPasClassScope;
|
|
if not (pcsfAncestorResolved in ClassScope.Flags) then
|
|
exit;
|
|
if SkipAlias then
|
|
begin
|
|
if ClassScope.AncestorScope=nil then
|
|
exit;
|
|
Result:=TPasClassType(ClassScope.AncestorScope.Element);
|
|
end
|
|
else
|
|
Result:=ClassScope.DirectAncestor;
|
|
end;
|
|
end;
|
|
|
|
function TPasResolver.GetLoop(El: TPasElement): TPasImplElement;
|
|
begin
|
|
while El<>nil do
|
|
begin
|
|
if (El.ClassType=TPasImplRepeatUntil)
|
|
or (El.ClassType=TPasImplWhileDo)
|
|
or (El.ClassType=TPasImplForLoop) then
|
|
exit(TPasImplElement(El));
|
|
El:=El.Parent;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TPasResolver.ResolveAliasType(aType: TPasType): TPasType;
|
|
var
|
|
C: TClass;
|
|
begin
|
|
Result:=aType;
|
|
while Result<>nil do
|
|
begin
|
|
C:=Result.ClassType;
|
|
if (C=TPasAliasType) or (C=TPasTypeAliasType) then
|
|
Result:=TPasAliasType(Result).DestType
|
|
else if (C=TPasClassType) and TPasClassType(Result).IsForward
|
|
and (Result.CustomData is TResolvedReference) then
|
|
Result:=TResolvedReference(Result.CustomData).Declaration as TPasType
|
|
else
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
function TPasResolver.ExprIsAddrTarget(El: TPasExpr): boolean;
|
|
{ returns true if El is
|
|
a) the last element of an @ operator expression
|
|
e.g. '@p().o[].El' or '@El[]'
|
|
b) mode delphi: the last element of a right side of an assignment
|
|
c) an accessor function, e.g. property P read El;
|
|
}
|
|
var
|
|
Parent: TPasElement;
|
|
Prop: TPasProperty;
|
|
begin
|
|
Result:=false;
|
|
if El=nil then exit;
|
|
if not IsNameExpr(El) then
|
|
exit;
|
|
repeat
|
|
Parent:=El.Parent;
|
|
//writeln('TPasResolver.ExprIsAddrTarget El=',GetObjName(El),' Parent=',GetObjName(Parent));
|
|
if Parent.ClassType=TUnaryExpr then
|
|
begin
|
|
if TUnaryExpr(Parent).OpCode=eopAddress then exit(true);
|
|
end
|
|
else if Parent.ClassType=TBinaryExpr then
|
|
begin
|
|
if TBinaryExpr(Parent).right<>El then exit;
|
|
if TBinaryExpr(Parent).OpCode<>eopSubIdent then exit;
|
|
end
|
|
else if Parent.ClassType=TParamsExpr then
|
|
begin
|
|
if TParamsExpr(Parent).Value<>El then exit;
|
|
end
|
|
else if Parent.ClassType=TPasProperty then
|
|
begin
|
|
Prop:=TPasProperty(Parent);
|
|
Result:=(Prop.ReadAccessor=El) or (Prop.WriteAccessor=El) or (Prop.StoredAccessor=El);
|
|
exit;
|
|
end
|
|
else if Parent.ClassType=TPasImplAssign then
|
|
begin
|
|
if TPasImplAssign(Parent).right<>El then exit;
|
|
if (msDelphi in CurrentParser.CurrentModeswitches) then exit(true);
|
|
exit;
|
|
end
|
|
else
|
|
exit;
|
|
El:=TPasExpr(Parent);
|
|
until false;
|
|
end;
|
|
|
|
function TPasResolver.GetLastExprIdentifier(El: TPasExpr): TPasExpr;
|
|
begin
|
|
Result:=El;
|
|
while Result<>nil do
|
|
begin
|
|
if Result is TParamsExpr then
|
|
Result:=TParamsExpr(Result).Value
|
|
else if Result is TBinaryExpr then
|
|
Result:=TBinaryExpr(Result).right;
|
|
end;
|
|
end;
|
|
|
|
function TPasResolver.ParentNeedsExprResult(El: TPasExpr): boolean;
|
|
var
|
|
C: TClass;
|
|
P: TPasElement;
|
|
begin
|
|
if (El=nil) or (El.Parent=nil) then exit(false);
|
|
Result:=false;
|
|
P:=El.Parent;
|
|
C:=P.ClassType;
|
|
if C.InheritsFrom(TPasExpr) then
|
|
Result:=true
|
|
else if (C=TPasEnumValue)
|
|
or (C=TPasArgument)
|
|
or (C=TPasVariable)
|
|
or (C=TPasExportSymbol) then
|
|
Result:=true
|
|
else if C=TPasClassType then
|
|
Result:=TPasClassType(P).GUIDExpr=El
|
|
else if C=TPasProperty then
|
|
Result:=(TPasProperty(P).IndexExpr=El)
|
|
or (TPasProperty(P).DispIDExpr=El)
|
|
or (TPasProperty(P).DefaultExpr=El)
|
|
else if C=TPasProcedure then
|
|
Result:=(TPasProcedure(P).LibraryExpr=El)
|
|
or (TPasProcedure(P).DispIDExpr=El)
|
|
else if C=TPasImplRepeatUntil then
|
|
Result:=(TPasImplRepeatUntil(P).ConditionExpr=El)
|
|
else if C=TPasImplIfElse then
|
|
Result:=(TPasImplIfElse(P).ConditionExpr=El)
|
|
else if C=TPasImplWhileDo then
|
|
Result:=(TPasImplWhileDo(P).ConditionExpr=El)
|
|
else if C=TPasImplWithDo then
|
|
Result:=(TPasImplWithDo(P).Expressions.IndexOf(El)>=0)
|
|
else if C=TPasImplCaseOf then
|
|
Result:=(TPasImplCaseOf(P).CaseExpr=El)
|
|
else if C=TPasImplCaseStatement then
|
|
Result:=(TPasImplCaseStatement(P).Expressions.IndexOf(El)>=0)
|
|
else if C=TPasImplForLoop then
|
|
Result:=(TPasImplForLoop(P).StartExpr=El)
|
|
or (TPasImplForLoop(P).EndExpr=El)
|
|
else if C=TPasImplAssign then
|
|
Result:=(TPasImplAssign(P).right=El)
|
|
else if C=TPasImplRaise then
|
|
Result:=(TPasImplRaise(P).ExceptAddr=El);
|
|
end;
|
|
|
|
function TPasResolver.GetReference_NewInstanceClass(Ref: TResolvedReference
|
|
): TPasClassType;
|
|
begin
|
|
Result:=(Ref.Context as TResolvedRefCtxConstructor).Typ as TPasClassType;
|
|
end;
|
|
|
|
function TPasResolver.IsDynArray(TypeEl: TPasType): boolean;
|
|
begin
|
|
if (TypeEl=nil) or (TypeEl.ClassType<>TPasArrayType)
|
|
or (length(TPasArrayType(TypeEl).Ranges)<>0) then
|
|
exit(false);
|
|
if proOpenAsDynArrays in Options then
|
|
Result:=true
|
|
else
|
|
Result:=(TypeEl.Parent=nil) or (TypeEl.Parent.ClassType<>TPasArgument);
|
|
end;
|
|
|
|
function TPasResolver.IsOpenArray(TypeEl: TPasType): boolean;
|
|
begin
|
|
Result:=(TypeEl<>nil)
|
|
and (TypeEl.ClassType=TPasArrayType)
|
|
and (length(TPasArrayType(TypeEl).Ranges)=0)
|
|
and (TypeEl.Parent<>nil)
|
|
and (TypeEl.Parent.ClassType=TPasArgument);
|
|
end;
|
|
|
|
function TPasResolver.IsDynOrOpenArray(TypeEl: TPasType): boolean;
|
|
begin
|
|
Result:=(TypeEl<>nil) and (TypeEl.ClassType=TPasArrayType)
|
|
and (length(TPasArrayType(TypeEl).Ranges)=0);
|
|
end;
|
|
|
|
function TPasResolver.IsClassMethod(El: TPasElement): boolean;
|
|
begin
|
|
Result:=(El<>nil)
|
|
and ((El.ClassType=TPasClassConstructor)
|
|
or (El.ClassType=TPasClassDestructor)
|
|
or (El.ClassType=TPasClassProcedure)
|
|
or (El.ClassType=TPasClassFunction)
|
|
or (El.ClassType=TPasClassOperator));
|
|
end;
|
|
|
|
function TPasResolver.IsExternalClassName(aClass: TPasClassType;
|
|
const ExtName: string): boolean;
|
|
var
|
|
AncestorScope: TPasClassScope;
|
|
begin
|
|
Result:=false;
|
|
if aClass=nil then exit;
|
|
while (aClass<>nil) and aClass.IsExternal do
|
|
begin
|
|
if aClass.ExternalName=ExtName then exit(true);
|
|
AncestorScope:=(aClass.CustomData as TPasClassScope).AncestorScope;
|
|
if AncestorScope=nil then exit;
|
|
aClass:=AncestorScope.Element as TPasClassType;
|
|
end;
|
|
end;
|
|
|
|
function TPasResolver.IsProcedureType(const ResolvedEl: TPasResolverResult;
|
|
HasValue: boolean): boolean;
|
|
begin
|
|
if (ResolvedEl.BaseType<>btContext) or not (ResolvedEl.TypeEl is TPasProcedureType) then
|
|
exit(false);
|
|
if HasValue and not (rrfReadable in ResolvedEl.Flags) then
|
|
exit(false);
|
|
Result:=true;
|
|
end;
|
|
|
|
function TPasResolver.IsArrayType(const ResolvedEl: TPasResolverResult
|
|
): boolean;
|
|
begin
|
|
Result:=(ResolvedEl.BaseType=btContext) and (ResolvedEl.TypeEl is TPasArrayType);
|
|
end;
|
|
|
|
function TPasResolver.IsTypeCast(Params: TParamsExpr): boolean;
|
|
var
|
|
Value: TPasExpr;
|
|
Ref: TResolvedReference;
|
|
Decl: TPasElement;
|
|
C: TClass;
|
|
begin
|
|
Result:=false;
|
|
if (Params=nil) or (Params.Kind<>pekFuncParams) then exit;
|
|
Value:=Params.Value;
|
|
if not IsNameExpr(Value) then
|
|
exit;
|
|
if not (Value.CustomData is TResolvedReference) then exit;
|
|
Ref:=TResolvedReference(Value.CustomData);
|
|
Decl:=Ref.Declaration;
|
|
C:=Decl.ClassType;
|
|
if (C=TPasAliasType) or (C=TPasTypeAliasType) then
|
|
begin
|
|
Decl:=ResolveAliasType(TPasAliasType(Decl));
|
|
C:=Decl.ClassType;
|
|
end;
|
|
if (C=TPasProcedureType)
|
|
or (C=TPasFunctionType) then
|
|
exit(true)
|
|
else if (C=TPasClassType)
|
|
or (C=TPasClassOfType)
|
|
or (C=TPasEnumType) then
|
|
exit(true)
|
|
else if (C=TPasUnresolvedSymbolRef)
|
|
and (Decl.CustomData is TResElDataBaseType) then
|
|
exit(true);
|
|
end;
|
|
|
|
function TPasResolver.ProcNeedsParams(El: TPasProcedureType): boolean;
|
|
begin
|
|
Result:=(El.Args.Count>0) and (TPasArgument(El.Args[0]).ValueExpr=nil);
|
|
end;
|
|
|
|
function TPasResolver.GetRangeLength(RangeResolved: TPasResolverResult
|
|
): integer;
|
|
begin
|
|
Result:=0;
|
|
if RangeResolved.BaseType=btContext then
|
|
begin
|
|
if RangeResolved.IdentEl is TPasEnumType then
|
|
Result:=TPasEnumType(RangeResolved.IdentEl).Values.Count;
|
|
end
|
|
else if RangeResolved.BaseType in btAllBooleans then
|
|
Result:=2;
|
|
end;
|
|
|
|
function TPasResolver.HasTypeInfo(El: TPasType): boolean;
|
|
begin
|
|
Result:=false;
|
|
if El=nil then exit;
|
|
if El.CustomData is TResElDataBaseType then
|
|
exit(true); // base type
|
|
if El.Parent=nil then exit;
|
|
if (El.Parent is TPasType) and not HasTypeInfo(TPasType(El.Parent)) then
|
|
exit;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TPasResolver.CheckSrcIsADstType(const ResolvedSrcType,
|
|
ResolvedDestType: TPasResolverResult; ErrorEl: TPasElement): integer;
|
|
// finds distance between classes SrcType and DestType
|
|
begin
|
|
Result:=CheckClassIsClass(ResolvedSrcType.TypeEl,ResolvedDestType.TypeEl,ErrorEl);
|
|
end;
|
|
|
|
function TPasResolver.CheckClassIsClass(SrcType, DestType: TPasType;
|
|
ErrorEl: TPasElement): integer;
|
|
// check if Src is equal or descends from Dest
|
|
var
|
|
ClassEl: TPasClassType;
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln('TPasResolver.CheckClassIsClass SrcType=',GetObjName(SrcType),' DestType=',GetObjName(DestType));
|
|
{$ENDIF}
|
|
if DestType=nil then exit(cIncompatible);
|
|
// skip Dest alias
|
|
while (DestType.ClassType=TPasAliasType) do
|
|
DestType:=TPasAliasType(DestType).DestType;
|
|
|
|
Result:=cExact;
|
|
while SrcType<>nil do
|
|
begin
|
|
{$IFDEF VerbosePasResolver}
|
|
writeln(' Step=',Result,' SrcType=',GetObjName(SrcType),' DestType=',GetObjName(DestType));
|
|
{$ENDIF}
|
|
if SrcType=DestType then
|
|
exit
|
|
else if SrcType.ClassType=TPasAliasType then
|
|
// alias -> skip
|
|
SrcType:=TPasAliasType(SrcType).DestType
|
|
else if SrcType.ClassType=TPasTypeAliasType then
|
|
begin
|
|
// type alias -> increases distance
|
|
SrcType:=TPasAliasType(SrcType).DestType;
|
|
inc(Result);
|
|
end
|
|
else if SrcType.ClassType=TPasClassType then
|
|
begin
|
|
ClassEl:=TPasClassType(SrcType);
|
|
if ClassEl.IsForward then
|
|
// class forward -> skip
|
|
SrcType:=(ClassEl.CustomData as TResolvedReference).Declaration as TPasType
|
|
else
|
|
begin
|
|
// class ancestor -> increase distance
|
|
SrcType:=(ClassEl.CustomData as TPasClassScope).DirectAncestor;
|
|
inc(Result);
|
|
end;
|
|
end
|
|
else
|
|
exit(cIncompatible);
|
|
end;
|
|
if ErrorEl=nil then ;
|
|
Result:=cIncompatible;
|
|
end;
|
|
|
|
function TPasResolver.CheckClassesAreRelated(TypeA, TypeB: TPasType;
|
|
ErrorEl: TPasElement): integer;
|
|
begin
|
|
Result:=CheckClassIsClass(TypeA,TypeB,ErrorEl);
|
|
if Result<>cIncompatible then exit;
|
|
Result:=CheckClassIsClass(TypeB,TypeA,ErrorEl);
|
|
end;
|
|
|
|
end.
|
|
|