Skip to content

Commit c5edeb1

Browse files
committed
fbc: sf.net # 948 Overriding class members doesn't work inside methods of further-extended subclasses
- internally, fbc differentiates between lookups in a NAMESPACE and lookups in a TYPE (method) Unqualified symbol lookups: - (1) current namespace/type - (2) base types (by 'Extends'), ranked from closest to furthest in inheritance hierarchy - (3) parent namespaces, ranked from closest to furthest in ancestry hierarchy (including global namespace: always furthest in hierarchy) - (4) imported namespaces (by 'Using'), without hierarchy between them
1 parent 56bc368 commit c5edeb1

File tree

11 files changed

+1053
-57
lines changed

11 files changed

+1053
-57
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ Version 1.09.0
9595
- sf.net #950: REDIM PRESERVE does not destroy the correct array elements of a multidimensional array -> memory leak
9696
- sf.net #945: regression: Scoping rules have changed inside methods - allow globals to shadow non-explicit enums implicitly imported in to the current namespace
9797
- sf.net #892: Bug when default calling via a pointer to a 'Sub' defined with the only 'Any Ptr' - fbc now differentiates between function pointers that have different numbers and positions of optional parameters
98+
- sf.net #948: Overriding class members doesn't work inside methods of further-extended subclasses
9899

99100

100101
Version 1.08.0

src/compiler/symb.bas

Lines changed: 189 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ declare sub symbKeywordTypeInit ( )
4848

4949
declare function hGetNamespacePrefix( byval sym as FBSYMBOL ptr ) as string
5050

51+
declare function hLookupImportList _
52+
( _
53+
byval ns as FBSYMBOL ptr, _
54+
byval id as const zstring ptr, _
55+
byval index as uinteger _
56+
) as FBSYMCHAIN ptr
57+
58+
5159
''globals
5260
dim shared as SYMBCTX symb
5361

@@ -869,6 +877,15 @@ function symbNewChainpool _
869877

870878
end function
871879

880+
/'
881+
!!!TODO!!! - clean up code for lookups
882+
refactor the duplicated code in:
883+
- symbLookup108()
884+
- symbLookupNS()
885+
- symbLookupTypeNS()
886+
- symbLookup()
887+
'/
888+
872889
'':::::
873890
function symbLookup108 _
874891
( _
@@ -943,15 +960,15 @@ function symbLookup108 _
943960
end function
944961

945962
'':::::
946-
function symbLookup _
963+
function symbLookupNS _
947964
( _
948965
byval id as zstring ptr, _
949966
byref tk as FB_TOKEN, _
950967
byref tk_class as FB_TKCLASS _
951968
) as FBSYMCHAIN ptr
952969

953970
static as zstring * FB_MAXNAMELEN+1 sname
954-
dim as FBSYMBOL ptr firstglobal = NULL
971+
dim as FBSYMBOL ptr first_ancestor = NULL
955972

956973
'' assume it's an unknown identifier
957974
tk = FB_TK_ID
@@ -966,7 +983,7 @@ function symbLookup _
966983
dim as FBHASHTB ptr hashtb = any
967984

968985
'' symbLookup() is used where we don't have an explicit namespace
969-
'' and we need to use the context of the lexer to start our search
986+
'' and we need to use the context of the lexer/parser to start our search
970987
''
971988
'' Make multiple passes on the hash lists to find the symbol.
972989
'' Because symbols are added in the order that they are defined,
@@ -991,10 +1008,6 @@ function symbLookup _
9911008
hashtb = hashtb->prev
9921009
loop while( hashtb <> NULL )
9931010

994-
'' use old lookup method from fbc 1.08.x and earlier?
995-
if( env.clopt.lookup108 ) then
996-
return symbLookup108( id, tk, tk_class )
997-
end if
9981011
'' any symbol not in the global namespace or we are already in the global namespace?
9991012
'' check the whole list before we check the imports
10001013
hashtb = symb.hashlist.tail
@@ -1011,8 +1024,8 @@ function symbLookup _
10111024
if( symbGetCurrentNamespc( ) = @symbGetGlobalNamespc( ) ) then
10121025
return symbNewChainpool( sym )
10131026
else
1014-
if( firstglobal = NULL ) then
1015-
firstglobal = sym
1027+
if( first_ancestor = NULL ) then
1028+
first_ancestor = sym
10161029
end if
10171030
end if
10181031
end if
@@ -1025,47 +1038,190 @@ function symbLookup _
10251038
dim as FBSYMCHAIN ptr imp_chain = hashLookupEx( @symb.imphashtb, id, index )
10261039
if( imp_chain <> NULL ) then
10271040

1028-
if( firstglobal ) then
1029-
'' If we have found both imports and a global, then check the imports.
1030-
'' If the import is actually a member of an enum, then we'd prefer to
1031-
'' return the global instead. If the anonymous enum is not the first
1032-
'' listed in the chain, then assume that the symbFindBy*() calls will
1033-
'' deal with finding a suitable symbol.
1041+
'' We have both an ancestor (which was reached directly in the current
1042+
'' namespace, plus we also have imports.
1043+
'' !!!TODO!!! scoped+non-eplicit enums are going to give us trouble here
1044+
'' because the enum member is imported in to the current namespace
1045+
'' The choices are:
1046+
'' - always return the first_ancestor found
1047+
'' - return the first_ancestor + imports, and give ambiguous errors
1048+
'' - something else where we search the imports for enums
1049+
'' For now, return the first ancestor and all imports
1050+
1051+
if( first_ancestor ) then
1052+
dim as FBSYMCHAIN ptr chain_ = symbNewChainpool( first_ancestor )
1053+
chain_->next = imp_chain
1054+
return chain_
1055+
end if
1056+
1057+
'' No ancestor? Just return the imports.
1058+
return imp_chain
1059+
end if
1060+
1061+
'' no imports? return only the first ancestor (if we already found it)
1062+
if( first_ancestor ) then
1063+
return symbNewChainpool( first_ancestor )
1064+
end if
1065+
1066+
'' never found
1067+
return NULL
1068+
1069+
end function
1070+
1071+
'':::::
1072+
function symbLookupTypeNS _
1073+
( _
1074+
byval id as zstring ptr, _
1075+
byref tk as FB_TOKEN, _
1076+
byref tk_class as FB_TKCLASS _
1077+
) as FBSYMCHAIN ptr
1078+
1079+
assert( symbGetCurrentNamespc( ) <> NULL )
1080+
1081+
static as zstring * FB_MAXNAMELEN+1 sname
1082+
dim as FBSYMBOL ptr first_ancestor = NULL
1083+
1084+
'' assume it's an unknown identifier
1085+
tk = FB_TK_ID
1086+
tk_class = FB_TKCLASS_IDENTIFIER
1087+
1088+
hUcase( *id, sname )
1089+
id = @sname
1090+
1091+
dim as uinteger index = hashHash( id )
1092+
1093+
'' for each nested hash tb, starting from last
1094+
dim as FBHASHTB ptr hashtb = any
1095+
1096+
'' symbLookup() is used where we don't have an explicit namespace
1097+
'' and we need to use the context of the lexer/parser to start our search
1098+
''
1099+
'' Make multiple passes on the hash lists to find the symbol.
1100+
'' Because symbols are added in the order that they are defined,
1101+
'' in some cases we can't just take the first symbol we find.
1102+
1103+
'' keyword?
1104+
hashtb = symb.hashlist.tail
1105+
do
1106+
dim as FBSYMBOL ptr sym = hashLookupEx( @hashtb->tb, id, index )
1107+
while( sym )
1108+
if( sym->class = FB_SYMBCLASS_KEYWORD ) then
1109+
tk = sym->key.id
1110+
tk_class = sym->key.tkclass
1111+
'' return if it's a KEYWORD or a OPERATOR token, they
1112+
'' can't never be redefined, even inside namespaces
1113+
if( tk_class <> FB_TKCLASS_QUIRKWD ) then
1114+
return symbNewChainpool( sym )
1115+
end if
1116+
end if
1117+
sym = sym->hash.next
1118+
wend
1119+
hashtb = hashtb->prev
1120+
loop while( hashtb <> NULL )
10341121

1035-
if( symbGetNamespace( imp_chain->sym ) <> NULL ) then
1036-
if( symbIsEnum( symbGetNamespace( imp_chain->sym ) ) ) then
1037-
return symbNewChainpool( firstglobal )
1122+
'' any symbol not in the global namespace or we are already in the global namespace?
1123+
'' check the whole list before we check the imports
1124+
hashtb = symb.hashlist.tail
1125+
do
1126+
dim as FBSYMBOL ptr sym = hashLookupEx( @hashtb->tb, id, index )
1127+
while( sym )
1128+
'' return if it's not the global ns (as in C++, any nested
1129+
'' symbol has precedence over any imported one, even if the
1130+
'' latter was imported in the current ns)
1131+
if( hashtb->owner <> @symbGetGlobalNamespc( ) ) then
1132+
return symbNewChainpool( sym )
1133+
else
1134+
'' also if we are at the global ns, no need to check the imports
1135+
if( symbGetCurrentNamespc( ) = @symbGetGlobalNamespc( ) ) then
1136+
return symbNewChainpool( sym )
1137+
else
1138+
if( first_ancestor = NULL ) then
1139+
first_ancestor = sym
1140+
end if
10381141
end if
10391142
end if
1143+
sym = sym->hash.next
1144+
wend
1145+
hashtb = hashtb->prev
1146+
loop while( hashtb <> NULL )
10401147

1041-
#if 0
1042-
/'
1043-
not yet entirely known is if we should return a symbchain for
1044-
the global plus the imports. This is what fbc < 1.09.0 did
1045-
but the look-ups were also broken then. This note is left here
1046-
as a hint if some future bug report is filed where we should expect
1047-
ambiguous access but didn't get it
1048-
'/
1049-
dim as FBSYMCHAIN ptr chain_ = symbNewChainpool( firstglobal )
1148+
dim as FBSYMBOL ptr ns = symbGetCurrentNamespc( )
1149+
1150+
'' search in UDT's (TYPE or CLASS) hash tb first
1151+
dim as FBSYMBOL ptr sym = hashLookupEx( @symbGetCompHashTb( ns ).tb, id, index )
1152+
1153+
'' don't access local variables in an explicit namespace
1154+
while( sym )
1155+
if( symbIsLocal( sym ) and symbIsVar( sym ) ) then
1156+
sym = sym->hash.next
1157+
else
1158+
exit while
1159+
end if
1160+
wend
1161+
1162+
'' Found the symbol in the type's namespace?
1163+
if( sym ) then
1164+
dim as FBSYMCHAIN ptr chain_ = symbNewChainpool( sym )
1165+
return chain_
1166+
end if
1167+
1168+
'' Search the type's imports
1169+
if( (symbGetCompExt( ns ) <> NULL) and (symbGetCompImportHead( ns ) <> NULL) ) then
1170+
dim as FBSYMCHAIN ptr chain_ = hLookupImportList( ns, id, index )
1171+
if( chain_ ) then
1172+
'' return the first one
1173+
return symbNewChainpool( chain_->sym )
1174+
end if
1175+
end if
1176+
1177+
'' search all the imports?
1178+
dim as FBSYMCHAIN ptr imp_chain = hashLookupEx( @symb.imphashtb, id, index )
1179+
if( imp_chain <> NULL ) then
1180+
1181+
if( first_ancestor ) then
1182+
dim as FBSYMCHAIN ptr chain_ = symbNewChainpool( first_ancestor )
10501183
chain_->next = imp_chain
10511184
return chain_
1052-
#endif
10531185
end if
10541186

1055-
'' if we didn't find any global, then return the imports
10561187
return imp_chain
1057-
endif
1188+
end if
10581189

1059-
'' no imports too? then return the global (if we already found it)
1060-
if( firstglobal ) then
1061-
return symbNewChainpool( firstglobal )
1190+
'' no imports? return only first ancestor (if we already found it)
1191+
if( first_ancestor ) then
1192+
return symbNewChainpool( first_ancestor )
10621193
end if
10631194

10641195
'' never found
10651196
return NULL
10661197

10671198
end function
10681199

1200+
'':::::
1201+
function symbLookup _
1202+
( _
1203+
byval id as zstring ptr, _
1204+
byref tk as FB_TOKEN, _
1205+
byref tk_class as FB_TKCLASS _
1206+
) as FBSYMCHAIN ptr
1207+
1208+
'' use old lookup method from fbc 1.08.x and earlier?
1209+
if( env.clopt.lookup108 ) then
1210+
return symbLookup108( id, tk, tk_class )
1211+
end if
1212+
1213+
'' In a TYPE's namespace?
1214+
if( symbGetCurrentNamespc( ) <> NULL ) then
1215+
select case symbGetClass( symbGetCurrentNamespc( ) )
1216+
case FB_SYMBCLASS_STRUCT, FB_SYMBCLASS_CLASS
1217+
return symbLookupTypeNS( id, tk, tk_class )
1218+
end select
1219+
end if
1220+
1221+
return symbLookupNS( id, tk, tk_class )
1222+
1223+
end function
1224+
10691225
'':::::
10701226
private function hLookupImportHash _
10711227
( _

tests/namespace/ns-using1.bas

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include once "fbcunit.bi"
2+
3+
#macro checkType( arg1, arg2 )
4+
#if( typeof(arg1) = typeof(arg2) )
5+
CU_PASS()
6+
#else
7+
CU_FAIL()
8+
#endif
9+
#endmacro
10+
11+
'' define namespace names to allow short names
12+
'' in this source but avoid namespace collisions
13+
'' in other sources
14+
15+
#define N1 ns_using1_N1
16+
#define N2 ns_using1_N2
17+
18+
namespace N1
19+
type T
20+
__ as long = 1
21+
end type
22+
23+
sub proc()
24+
checkType( T, N1.T )
25+
end sub
26+
end namespace
27+
28+
namespace N2
29+
using N1
30+
31+
type T
32+
__ as long = 2
33+
end type
34+
35+
dim as T foo
36+
37+
sub proc()
38+
checkType( T, N2.T )
39+
end sub
40+
end namespace
41+
42+
private sub module_proc()
43+
N1.proc()
44+
N2.proc()
45+
46+
checkType( N1.T, N1.T )
47+
checkType( N2.T, N2.T )
48+
49+
using N2
50+
51+
checkType( T, N2.T )
52+
53+
CU_ASSERT_EQUAL( foo.__, 2 )
54+
end sub
55+
56+
SUITE( fbc_tests.namespace_.ns_using1 )
57+
TEST( default )
58+
module_proc()
59+
END_TEST
60+
END_SUITE

0 commit comments

Comments
 (0)