Skip to content

Commit 10dd3dd

Browse files
committed
parser: Fix/improve error recovery for procedure calls
This fixes a compiler crash during error recovery for BYREF AS ANY parameters and (in theory) improves error recovery for procedure calls. http://www.freebasic.net/forum/viewtopic.php?f=3&t=24140 The parser calls astNewCONSTz() to build some argument expression for error recovery, but for ANY parameters, that returned NULL, causing crashes later in astNewARG() which doesn't expect NULL. Besides BYREF AS ANY parameters, it looks like astNewCONSTz() can only be called with ANY type in case of SUBs (which internally use FB_DATATYPE_VOID as type). This patch changes the parser to build "proper" CALL expressions for error recovery in the call parser, and it also changes astNewCONSTz() to never return NULL anymore. If an ANY expression is requested, it just assumes to be able to return an expression with any type. This also means that cProcArgList() doesn't ever return NULL or a CONST node anymore, but always a CALL node, which allows some further clean-ups.
1 parent 30fd65a commit 10dd3dd

File tree

10 files changed

+58
-97
lines changed

10 files changed

+58
-97
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Version 1.05.0
1515
- #print typeof() output now differentiates between ZSTRING and ZSTRING * N (ZSTRING without size is produced by dereferencing a ZSTRING PTR, or BYREF AS ZSTRING)
1616
- Context-specific keywords, e.g. graphics PUT modes, must now be given as keywords (e.g. PSET), string literals (e.g. "PSET") are no longer accepted.
1717
- Wstring-to-Zstring conversions didn't use the system's Unicode <-> codepage conversion function, and only converted ASCII characters. Now it will try to convert the Unicode chars to codepage chars.
18+
- Compiler crash during error recovery when there was an error when parsing the argument expression for a BYREF AS ANY parameter
1819

1920

2021
Version 1.04.0

src/compiler/ast-node-call.bas

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,3 +531,17 @@ function astIgnoreCallResult( byval n as ASTNODE ptr ) as ASTNODE ptr
531531
astSetType( n, FB_DATATYPE_VOID, NULL )
532532
function = n
533533
end function
534+
535+
'' Build a fake CALL to the given procedure, to be used for error recovery.
536+
function astBuildFakeCall( byval proc as FBSYMBOL ptr ) as ASTNODE ptr
537+
var c = astNewCALL( proc )
538+
539+
'' Build fake arg for each param (including the THIS param for methods)
540+
var param = symbGetProcHeadParam( proc )
541+
while( param )
542+
astNewARG( c, astNewCONSTz( symbGetType( param ), param->subtype ) )
543+
param = param->next
544+
wend
545+
546+
function = c
547+
end function

src/compiler/ast-node-const.bas

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,9 @@ function astNewCONSTz _
125125
select case as const( typeGetDtAndPtrOnly( dtype ) )
126126
case FB_DATATYPE_VOID
127127
'' A CONST expression with VOID type doesn't make sense,
128-
'' but some callers of astNewCONSTz() can call it for SUBs,
129-
'' which have a VOID result type.
130-
function = NULL
128+
'' but astNewCONSTz() can be called for BYREF AS ANY parameters.
129+
'' Any expression type should be ok for error recovery.
130+
function = astNewCONSTi( 0 )
131131

132132
case FB_DATATYPE_STRING, FB_DATATYPE_FIXSTR, FB_DATATYPE_CHAR
133133
function = astNewCONSTstr( NULL )

src/compiler/ast.bi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,7 @@ declare function astBuildByrefResultDeref( byval expr as ASTNODE ptr ) as ASTNOD
13071307
declare function astIsByrefResultDeref( byval expr as ASTNODE ptr ) as integer
13081308
declare function astRemoveByrefResultDeref( byval expr as ASTNODE ptr ) as ASTNODE ptr
13091309
declare function astIgnoreCallResult( byval n as ASTNODE ptr ) as ASTNODE ptr
1310+
declare function astBuildFakeCall( byval proc as FBSYMBOL ptr ) as ASTNODE ptr
13101311

13111312
declare sub astGosubAddInit( byval proc as FBSYMBOL ptr )
13121313

src/compiler/parser-expr-function.bas

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,6 @@ function cFunctionCall _
5656
lexSkipToken( )
5757

5858
funcexpr = cProcArgList( base_parent, sym, ptrexpr, @arg_list, options )
59-
if( funcexpr = NULL ) then
60-
exit function
61-
end if
6259

6360
'' ')'
6461
hParseRPNT( )
@@ -70,14 +67,8 @@ function cFunctionCall _
7067
end if
7168

7269
'' no args
73-
funcexpr = cProcArgList( base_parent, _
74-
sym, _
75-
ptrexpr, _
76-
@arg_list, _
77-
options or FB_PARSEROPT_OPTONLY )
78-
if( funcexpr = NULL ) then
79-
exit function
80-
end if
70+
funcexpr = cProcArgList( base_parent, sym, ptrexpr, @arg_list, _
71+
options or FB_PARSEROPT_OPTONLY )
8172
end if
8273

8374
else
@@ -87,23 +78,14 @@ function cFunctionCall _
8778

8879
'' ProcArgList
8980
funcexpr = cProcArgList( base_parent, sym, ptrexpr, @arg_list, options )
90-
if( funcexpr = NULL ) then
91-
exit function
92-
end if
9381

9482
'' ')'
9583
hParseRPNT( )
9684

9785
else
9886
'' ProcArgList (function could have optional params)
99-
funcexpr = cProcArgList( base_parent, _
100-
sym, _
101-
ptrexpr, _
102-
@arg_list, _
103-
options or FB_PARSEROPT_OPTONLY )
104-
if( funcexpr = NULL ) then
105-
exit function
106-
end if
87+
funcexpr = cProcArgList( base_parent, sym, ptrexpr, @arg_list, _
88+
options or FB_PARSEROPT_OPTONLY )
10789
end if
10890

10991
end if
@@ -198,15 +180,9 @@ function cCtorCall _
198180
arg->expr = astNewVAR( tmp )
199181
arg->mode = INVALID
200182

201-
procexpr = cProcArgList( NULL, _
202-
symbGetCompCtorHead( sym ), _
203-
NULL, _
204-
@arg_list, _
205-
FB_PARSEROPT_ISFUNC or _
206-
FB_PARSEROPT_HASINSTPTR or _
207-
iif( isprnt = FALSE, _
208-
FB_PARSEROPT_OPTONLY, _
209-
FB_PARSEROPT_NONE ) )
183+
procexpr = cProcArgList( NULL, symbGetCompCtorHead( sym ), NULL, @arg_list, _
184+
FB_PARSEROPT_ISFUNC or FB_PARSEROPT_HASINSTPTR or _
185+
iif( isprnt = FALSE, FB_PARSEROPT_OPTONLY, FB_PARSEROPT_NONE ) )
210186

211187
'' Try to parse the ')' even in case of error recovery; it belongs to
212188
'' the type() construct afterall, and nothing else would handle it.
@@ -222,13 +198,5 @@ function cCtorCall _
222198
end if
223199
end if
224200

225-
'' cProcArgList() usually returns a CALL, but it can be NULL or a CONST
226-
'' etc. in case of error recovery
227-
if( procexpr = NULL ) then
228-
function = NULL
229-
elseif( astIsCALL( procexpr ) = FALSE ) then
230-
function = procexpr
231-
else
232-
function = astNewCALLCTOR( procexpr, astNewVAR( tmp ) )
233-
end if
201+
function = astNewCALLCTOR( procexpr, astNewVAR( tmp ) )
234202
end function

src/compiler/parser-proccall-args.bas

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ private function hOvlProcArgList _
362362

363363
errReportParam( proc, 0, NULL, err_num )
364364
'' error recovery: fake an expr
365-
return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
365+
return astBuildFakeCall( proc )
366366
end if
367367

368368
proc = ovlproc
@@ -374,7 +374,7 @@ private function hOvlProcArgList _
374374
FB_ERRMSG_ILLEGALMEMBERACCESS ), _
375375
symbGetFullProcName( proc ) )
376376
'' error recovery: fake an expr
377-
return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
377+
return astBuildFakeCall( proc )
378378
end if
379379

380380
'' method?
@@ -386,7 +386,7 @@ private function hOvlProcArgList _
386386
if( (base_parent <> NULL) or (symbIsMethod( parser.currproc ) = FALSE) ) then
387387
errReport( FB_ERRMSG_MEMBERISNTSTATIC, TRUE )
388388
'' error recovery: fake an expr
389-
return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
389+
return astBuildFakeCall( proc )
390390
end if
391391

392392
'' pass the instance ptr of the current method
@@ -422,7 +422,8 @@ private function hOvlProcArgList _
422422
errReport( FB_ERRMSG_PARAMTYPEMISMATCH )
423423
'' error recovery: fake an expr (don't try to fake an arg,
424424
'' different modes and param types like "as any" would break AST)
425-
return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
425+
astDelTree( procexpr )
426+
return astBuildFakeCall( proc )
426427
end if
427428

428429
symbFreeOvlCallArg( @parser.ovlarglist, arg )
@@ -448,6 +449,9 @@ end function
448449
'':::::
449450
''ProcArgList = ProcArg (DECL_SEPARATOR ProcArg)* .
450451
''
452+
'' This function always returns a CALL, even for error recovery - so callers
453+
'' don't have to check before passing it to astNewCALLCTOR() etc.
454+
''
451455
function cProcArgList _
452456
( _
453457
byval base_parent as FBSYMBOL ptr, _
@@ -480,7 +484,7 @@ function cProcArgList _
480484
FB_ERRMSG_ILLEGALMEMBERACCESS ), _
481485
symbGetFullProcName( proc ) )
482486
'' error recovery: fake an expr
483-
return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
487+
return astBuildFakeCall( proc )
484488
end if
485489

486490
'' method?
@@ -492,7 +496,7 @@ function cProcArgList _
492496
if( (base_parent <> NULL) or (symbIsMethod( parser.currproc ) = FALSE) ) then
493497
errReport( FB_ERRMSG_MEMBERISNTSTATIC, TRUE )
494498
'' error recovery: fake an expr
495-
return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
499+
return astBuildFakeCall( proc )
496500
end if
497501

498502
'' pass the instance ptr of the current method
@@ -529,7 +533,8 @@ function cProcArgList _
529533
dim as FB_CALL_ARG ptr nxt = arg->next
530534

531535
if( astNewARG( procexpr, arg->expr, , arg->mode ) = NULL ) then
532-
exit function
536+
astDelTree( procexpr )
537+
return astBuildFakeCall( proc )
533538
end if
534539

535540
symbFreeOvlCallArg( @parser.ovlarglist, arg )
@@ -597,7 +602,7 @@ function cProcArgList _
597602
'' don't try to fake an arg, different modes and param
598603
'' types like "as any" would break AST
599604
astDelTree( procexpr )
600-
return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
605+
return astBuildFakeCall( proc )
601606
end if
602607

603608
'' next
@@ -624,12 +629,13 @@ function cProcArgList _
624629
errReport( FB_ERRMSG_ARGCNTMISMATCH )
625630
'' error recovery: fake an expr
626631
astDelTree( procexpr )
627-
return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
632+
return astBuildFakeCall( proc )
628633
end if
629634

630635
'' add to tree
631636
if( astNewARG( procexpr, NULL ) = NULL ) then
632-
exit function
637+
astDelTree( procexpr )
638+
return astBuildFakeCall( proc )
633639
end if
634640

635641
'' next

src/compiler/parser-proccall.bas

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -349,10 +349,6 @@ function cProcCall _
349349

350350
'' ProcArgList
351351
procexpr = cProcArgList( base_parent, sym, ptrexpr, @arg_list, options )
352-
if( procexpr = NULL ) then
353-
hSkipUntil( CHAR_RPRNT )
354-
exit function
355-
end if
356352

357353
'' ')'
358354
if( (checkprnts) or (parser.prntcnt > 0) ) then
@@ -974,28 +970,19 @@ private sub hBaseInit( )
974970
lexSkipToken( )
975971

976972
subtype = symbGetSubtype( base_ )
977-
initree = NULL
978973

979974
'' Has a ctor?
980975
if( symbGetCompCtorHead( subtype ) ) then
981976
'' CtorCall
982977
ctorcall = cCtorCall( subtype )
983-
if( ctorcall ) then
984-
'' Will be a CTORCALL except in case of error recovery
985-
if( astIsCALLCTOR( ctorcall ) ) then
986-
'' cCtorCall() created a temporary object to
987-
'' call the constructor on, we delete it though:
988-
ctorcall = astCALLCTORToCALL( ctorcall )
989-
990-
'' Turn the ctorcall into an initree
991-
initree = astTypeIniBegin( FB_DATATYPE_STRUCT, subtype, TRUE )
992-
astTypeIniAddCtorCall( initree, base_, ctorcall )
993-
astTypeIniEnd( initree, TRUE )
994-
else
995-
astDelTree( ctorcall )
996-
ctorcall = NULL
997-
end if
998-
end if
978+
'' cCtorCall() created a temporary object to
979+
'' call the constructor on, we delete it though:
980+
ctorcall = astCALLCTORToCALL( ctorcall )
981+
982+
'' Turn the ctorcall into an initree
983+
initree = astTypeIniBegin( FB_DATATYPE_STRUCT, subtype, TRUE )
984+
astTypeIniAddCtorCall( initree, base_, ctorcall )
985+
astTypeIniEnd( initree, TRUE )
999986
else
1000987
'' Initializer
1001988
initree = cInitializer( base_, FB_INIOPT_ISINI )
@@ -1153,16 +1140,6 @@ function hForwardCall( ) as integer
11531140
exit function
11541141
end if
11551142

1156-
''
1157-
dim as ASTNODE ptr procexpr = cProcArgList( NULL, _
1158-
proc, _
1159-
NULL, _
1160-
@arg_list, _
1161-
FB_PARSEROPT_OPTONLY )
1162-
if( procexpr <> NULL ) then
1163-
astAdd( procexpr )
1164-
end if
1165-
1143+
astAdd( cProcArgList( NULL, proc, NULL, @arg_list, FB_PARSEROPT_OPTONLY ) )
11661144
function = TRUE
1167-
11681145
end function

src/compiler/parser-quirk-casting.bas

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -199,12 +199,7 @@ function cAnonType( ) as ASTNODE ptr
199199
if( typeGetDtAndPtrOnly( dtype ) = FB_DATATYPE_STRUCT ) then
200200
'' Has a ctor?
201201
if( symbGetCompCtorHead( subtype ) ) then
202-
initree = cCtorCall( subtype )
203-
if( initree = NULL ) then
204-
'' Error recovery
205-
initree = astNewCONSTz( dtype, subtype )
206-
end if
207-
return initree
202+
return cCtorCall( subtype )
208203
end if
209204
end if
210205

src/compiler/parser-quirk-mem.bas

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,6 @@ function cOperatorNew( ) as ASTNODE ptr
134134
errReport( FB_ERRMSG_EXPLICITCTORCALLINVECTOR, TRUE )
135135
else
136136
initexpr = cCtorCall( subtype )
137-
if( initexpr = NULL ) then
138-
return NULL
139-
end if
140137
end if
141138
else
142139
dim as FBSYMBOL ptr ctor = symbGetCompDefCtor( subtype )
@@ -147,9 +144,6 @@ function cOperatorNew( ) as ASTNODE ptr
147144
'' only if not a vector
148145
if( op <> AST_OP_NEW_VEC ) then
149146
initexpr = cCtorCall( subtype )
150-
if( initexpr = NULL ) then
151-
return NULL
152-
end if
153147
else
154148
'' check visibility
155149
if( symbCheckAccess( ctor ) = FALSE ) then
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
' TEST_MODE : COMPILE_ONLY_FAIL
2+
3+
'' Testing error recovery while parsing the argument expression for a
4+
'' BYREF AS ANY parameter
5+
clear tests(0), , 1

0 commit comments

Comments
 (0)