default properties: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: default properties
| Old | New | Line | |
|---|---|---|---|
| 1 | + | Default properties allow "hoisting" or exposing of object members into the caller name space to facilitate wrapper types and (possibly) delegation patterns. They could be seen as "with" statements which encompass an entire structures namespace. | |
| 2 | + | ||
| 3 | + | This feature is under development and everything is subject to change. | |
| 4 | + | ||
| 5 | + | ## Contents | |
| 6 | + | ||
| 7 | + | * 1 Download Development Branch | |
| 8 | + | * 2 Supports | |
| 9 | + | * 3 Hoisting Members | |
| 10 | + | * 4 Precedence Rules | |
| 11 | + | * 5 Passing | |
| 12 | + | * 6 Assignment Rules | |
| 13 | + | * 7 Multiple Defaults | |
| 14 | + | * 8 Hiding Fields | |
| 15 | + | * 9 Examples Usages | |
| 16 | + | * 9.1 Nullable types | |
| 17 | + | * 9.2 Auto managed classes using management operators | |
| 18 | + | * 9.3 Array indexing | |
| 19 | + | * 9.4 Delegation | |
| 20 | + | * 9.5 Default "Implements" Properties | |
| 21 | + | * 10 COMPILER: Implemention Details | |
| 22 | + | ||
| 23 | + | ## Download Development Branch | |
| 24 | + | ||
| 25 | + | <https://github.com/genericptr/freepascal/tree/defaultprops> | |
| 26 | + | ||
| 27 | + | ## Supports | |
| 28 | + | ||
| 29 | + | * records, objects, classes. | |
| 30 | + | * arithmetic, compare, binary, unary, in overloads. (<https://www.freepascal.org/docs-html/ref/refch15.html>) | |
| 31 | + | * visibility sections. | |
| 32 | + | * array indexing with [] | |
| 33 | + | * if, while, repeat, case, for..do statements | |
| 34 | + | ||
| 35 | + | ## Hoisting Members | |
| 36 | + | ||
| 37 | + | By making "m_obj" a default property we can omit m_obj. to subscript into the members. This is like wrapping every instance of "m_obj" in a "with" statement. This makes it possible to bring members from other structures into the current structure without prefixing "m_obj." before accessing fields. | |
| 38 | + | ``` | |
| 39 | + | type | |
| 40 | + | THelper = record | |
| 41 | + | num: integer; | |
| 42 | + | end; | |
| 43 | + | ||
| 44 | + | type | |
| 45 | + | TWrapper = record | |
| 46 | + | m_obj: THelper; | |
| 47 | + | property helper: THelper read m_obj; default; | |
| 48 | + | procedure Inc; | |
| 49 | + | end; | |
| 50 | + | ||
| 51 | + | procedure TWrapper.Inc; | |
| 52 | + | begin | |
| 53 | + | num += 1; | |
| 54 | + | end; | |
| 55 | + | ||
| 56 | + | var | |
| 57 | + | wrapper: TWrapper; | |
| 58 | + | begin | |
| 59 | + | wrapper.num := 100; | |
| 60 | + | wrapper.Inc; | |
| 61 | + | writeln(wrapper.num); | |
| 62 | + | end. | |
| 63 | + | ``` | |
| 64 | + | ||
| 65 | + | ## Precedence Rules | |
| 66 | + | ||
| 67 | + | For default properties which are structures (objects, records, classes) it's possible to have method overloads in both the base structure and the default property (or unit scope even). For this reason overload resolution must follow a predicable pattern. | |
| 68 | + | ||
| 69 | + | Precedence order for overloads is as follows: base, default (last to first). | |
| 70 | + | ``` | |
| 71 | + | type | |
| 72 | + | THelper = class | |
| 73 | + | num: integer; | |
| 74 | + | procedure DoThis; overload; | |
| 75 | + | procedure DoThis (param: integer); overload; | |
| 76 | + | procedure DoThis (param: string); overload; | |
| 77 | + | end; | |
| 78 | + | ||
| 79 | + | procedure THelper.DoThis; | |
| 80 | + | begin | |
| 81 | + | writeln('THelper.DoThis:',num); | |
| 82 | + | end; | |
| 83 | + | ||
| 84 | + | procedure THelper.DoThis (param: integer); | |
| 85 | + | begin | |
| 86 | + | writeln('THelper.DoThis:',param,':',num); | |
| 87 | + | end; | |
| 88 | + | ||
| 89 | + | procedure THelper.DoThis (param: string); | |
| 90 | + | begin | |
| 91 | + | writeln('THelper.DoThis:',param,':',num); | |
| 92 | + | end; | |
| 93 | + | ||
| 94 | + | type | |
| 95 | + | TParent = class | |
| 96 | + | m_helper: THelper; | |
| 97 | + | property helper: THelper read m_helper; default; | |
| 98 | + | procedure DoThis (param:single);overload; | |
| 99 | + | end; | |
| 100 | + | ||
| 101 | + | procedure TParent.DoThis (param:single); | |
| 102 | + | begin | |
| 103 | + | writeln('TParent.DoThis:',param:1:1); | |
| 104 | + | end; | |
| 105 | + | ||
| 106 | + | type | |
| 107 | + | TMyObject = class (TParent) | |
| 108 | + | procedure Call; | |
| 109 | + | procedure DoThis;overload; | |
| 110 | + | end; | |
| 111 | + | ||
| 112 | + | procedure TMyObject.DoThis; | |
| 113 | + | begin | |
| 114 | + | writeln('TMyObject.DoThis'); | |
| 115 | + | end; | |
| 116 | + | ||
| 117 | + | procedure TMyObject.Call; | |
| 118 | + | begin | |
| 119 | + | DoThis(100); // THelper.DoThis | |
| 120 | + | DoThis(10.5); // TParent.DoThis | |
| 121 | + | DoThis('hello'); // THelper.DoThis | |
| 122 | + | DoThis; // TMyObject.DoThis | |
| 123 | + | end; | |
| 124 | + | ||
| 125 | + | var | |
| 126 | + | obj: TMyObject; | |
| 127 | + | begin | |
| 128 | + | obj := TMyObject.Create; | |
| 129 | + | obj.m_helper := THelper.Create; | |
| 130 | + | obj.Call; | |
| 131 | + | end. | |
| 132 | + | ``` | |
| 133 | + | ||
| 134 | + | Operator overloads are possible also. Precedence can get complicated considering it's possible to have overloads in the current unit for the default type as well as overloads in the structure itself. | |
| 135 | + | ``` | |
| 136 | + | operator + (l : integer; r : ansistring): integer; | |
| 137 | + | begin | |
| 138 | + | result := l + StrToInt(r); | |
| 139 | + | end; | |
| 140 | + | ||
| 141 | + | type | |
| 142 | + | TWrapper = record | |
| 143 | + | m_value: integer; | |
| 144 | + | property value: integer read m_value write m_value; default; | |
| 145 | + | end; | |
| 146 | + | ||
| 147 | + | var | |
| 148 | + | wrapper: TWrapper; | |
| 149 | + | begin | |
| 150 | + | wrapper += 1; // default property takes precedence | |
| 151 | + | wrapper += '128'; // unit + operator takes precedence because default property type (integer) doesn't support string overloads | |
| 152 | + | writeln(wrapper.value); // 129 | |
| 153 | + | if wrapper = 129 then // = operator works also | |
| 154 | + | writeln('got 129'); | |
| 155 | + | end. | |
| 156 | + | ``` | |
| 157 | + | ||
| 158 | + | Type helpers behave as normal on the default type. | |
| 159 | + | ``` | |
| 160 | + | type | |
| 161 | + | TIntegerHelper = type helper for integer | |
| 162 | + | function Str: string; | |
| 163 | + | end; | |
| 164 | + | ||
| 165 | + | function TIntegerHelper.Str: string; | |
| 166 | + | begin | |
| 167 | + | result := 'string->'+IntToStr(self); | |
| 168 | + | end; | |
| 169 | + | ||
| 170 | + | type | |
| 171 | + | TWrapper = record | |
| 172 | + | m_value: integer; | |
| 173 | + | property value: integer read m_value write m_value; default; | |
| 174 | + | end; | |
| 175 | + | ||
| 176 | + | var | |
| 177 | + | wrapper: TWrapper; | |
| 178 | + | begin | |
| 179 | + | wrapper := 100; | |
| 180 | + | writeln(wrapper.str); | |
| 181 | + | end. | |
| 182 | + | ``` | |
| 183 | + | ||
| 184 | + | ## Passing | |
| 185 | + | ||
| 186 | + | Passing a structure with default properties follows normal move semantics. For example, if there is a record with a default property of the type "integer" passing the record will not copy the integer value but rather the entire record will be passed. | |
| 187 | + | ``` | |
| 188 | + | type | |
| 189 | + | TIntRec = record | |
| 190 | + | m_int: integer; | |
| 191 | + | property value: integer read m_obj; default; | |
| 192 | + | end; | |
| 193 | + | ||
| 194 | + | procedure Print (int: TIntRec); overload; | |
| 195 | + | begin | |
| 196 | + | writeln(int.value); | |
| 197 | + | end; | |
| 198 | + | ||
| 199 | + | procedure Print (int: integer); overload; | |
| 200 | + | begin | |
| 201 | + | writeln(int); | |
| 202 | + | end; | |
| 203 | + | ||
| 204 | + | var | |
| 205 | + | int: TIntRec; | |
| 206 | + | begin | |
| 207 | + | int := 100; | |
| 208 | + | Print(int); // Print(TIntRec) is called | |
| 209 | + | writeln(int) // ERROR: can't write this type (TIntRec) | |
| 210 | + | end. | |
| 211 | + | ``` | |
| 212 | + | ||
| 213 | + | ## Assignment Rules | |
| 214 | + | ||
| 215 | + | Default properties introduce an ambiguity with assignments which is resolved by type. | |
| 216 | + | ``` | |
| 217 | + | type | |
| 218 | + | TWrapper = class | |
| 219 | + | m_value: integer; | |
| 220 | + | m_string: string; | |
| 221 | + | property value: integer read m_value write m_value; default; | |
| 222 | + | end; | |
| 223 | + | ||
| 224 | + | var | |
| 225 | + | wrapper: TWrapper; | |
| 226 | + | begin | |
| 227 | + | wrapper := TWrapper.Create; // TWrapper is same type so assignment is as normal | |
| 228 | + | wrapper := 100; // integer matches "value" | |
| 229 | + | writeln(wrapper.value); | |
| 230 | + | end. | |
| 231 | + | ``` | |
| 232 | + | ||
| 233 | + | ## Multiple Defaults | |
| 234 | + | ||
| 235 | + | Currently for development default properties are "multidimensional", meaning you can declare more than one per type and overloads will be resolved according to precedence. | |
| 236 | + | ||
| 237 | + | They have been developed this way from the outset because the nature of the search is inherently multidimensional, i.e base object, default property 1, default property 2 etc... so I wanted to keep the option available while the concept is being explored. | |
| 238 | + | ||
| 239 | + | This is of course a highly contested idea given the capacity to introduce difficult to understand namespace collisions. | |
| 240 | + | ||
| 241 | + | ## Hiding Fields | |
| 242 | + | ||
| 243 | + | If multiple defaults are allowed then it would be possible to hide fields from other default properties and cause some potential bugs. | |
| 244 | + | ``` | |
| 245 | + | type | |
| 246 | + | THelper = record | |
| 247 | + | data: integer; | |
| 248 | + | end; | |
| 249 | + | ||
| 250 | + | type | |
| 251 | + | TWrapper = record | |
| 252 | + | m_objA: THelper; | |
| 253 | + | m_objB: THelper; | |
| 254 | + | property helperA: THelper read m_objA; default; | |
| 255 | + | property helperB: THelper read m_objB; default; | |
| 256 | + | end; | |
| 257 | + | ||
| 258 | + | var | |
| 259 | + | wrapper: TWrapper; | |
| 260 | + | begin | |
| 261 | + | wrapper.data := 1; | |
| 262 | + | writeln('helperA:',wrapper.helperA.data); // 0 | |
| 263 | + | writeln('helperB:',wrapper.helperB.data); // 1 | |
| 264 | + | end. | |
| 265 | + | ``` | |
| 266 | + | ||
| 267 | + | ## Examples Usages | |
| 268 | + | ||
| 269 | + | ### Nullable types | |
| 270 | + | ``` | |
| 271 | + | type | |
| 272 | + | generic TNullable<T>= record | |
| 273 | + | private | |
| 274 | + | FValue : T; | |
| 275 | + | public | |
| 276 | + | isAssigned : boolean; | |
| 277 | + | function IsNull: boolean; | |
| 278 | + | procedure SetValue (newValue: T); | |
| 279 | + | Property Value : T read FValue write SetValue; default; | |
| 280 | + | class operator Initialize(var a: TNullable); | |
| 281 | + | end; | |
| 282 | + | ||
| 283 | + | procedure TNullable.SetValue (newValue: T); | |
| 284 | + | begin | |
| 285 | + | FValue := newValue; | |
| 286 | + | isAssigned := true; | |
| 287 | + | end; | |
| 288 | + | ||
| 289 | + | function TNullable.IsNull: boolean; | |
| 290 | + | begin | |
| 291 | + | result := not isAssigned; | |
| 292 | + | end; | |
| 293 | + | ||
| 294 | + | class operator TNullable.Initialize(var a: TNullable); | |
| 295 | + | begin | |
| 296 | + | a.isAssigned := Default(T); | |
| 297 | + | end; | |
| 298 | + | ||
| 299 | + | type | |
| 300 | + | TBoolean = specialize TNullable<boolean>; | |
| 301 | + | Var | |
| 302 | + | bool: TBoolean; | |
| 303 | + | begin | |
| 304 | + | bool := true; | |
| 305 | + | if bool then | |
| 306 | + | writeln('bool is true'); | |
| 307 | + | end. | |
| 308 | + | ``` | |
| 309 | + | ||
| 310 | + | ### Auto managed classes using management operators | |
| 311 | + | ``` | |
| 312 | + | type | |
| 313 | + | generic TAuto<T> = record | |
| 314 | + | private | |
| 315 | + | m_object: T; | |
| 316 | + | class operator Initialize(var a: TAuto); | |
| 317 | + | class operator Finalize(var a: TAuto); | |
| 318 | + | public | |
| 319 | + | property obj: T read m_object; default; | |
| 320 | + | end; | |
| 321 | + | ||
| 322 | + | type | |
| 323 | + | TStringList = specialize TFPGList<String>; | |
| 324 | + | TStringListAuto = specialize TAuto<TStringList>; | |
| 325 | + | ||
| 326 | + | class operator TAuto.Initialize(var a: TAuto); | |
| 327 | + | begin | |
| 328 | + | a.m_object := T.Create; | |
| 329 | + | end; | |
| 330 | + | ||
| 331 | + | class operator TAuto.Finalize(var a: TAuto); | |
| 332 | + | begin | |
| 333 | + | a.m_object.Free; | |
| 334 | + | end; | |
| 335 | + | ||
| 336 | + | var | |
| 337 | + | list: TStringListAuto; | |
| 338 | + | str: string; | |
| 339 | + | begin | |
| 340 | + | list.Add('foo'); | |
| 341 | + | list.Add('bar'); | |
| 342 | + | for str in list do | |
| 343 | + | writeln(str); | |
| 344 | + | end. | |
| 345 | + | ``` | |
| 346 | + | ||
| 347 | + | ### Array indexing | |
| 348 | + | ||
| 349 | + | Because it is technically possible arrays are exposed using default properties. This however does conflict with the existing [] indexer properties so this may be a problem. | |
| 350 | + | ||
| 351 | + | As it stands however default properties on array types are an interesting method to make array wrappers. | |
| 352 | + | ``` | |
| 353 | + | type | |
| 354 | + | TIntArray = array of integer; | |
| 355 | + | TWrapper = record | |
| 356 | + | m_value: TIntArray; | |
| 357 | + | property value: TIntArray read m_value write m_value; default; | |
| 358 | + | end; | |
| 359 | + | ||
| 360 | + | var | |
| 361 | + | wrapper: TWrapper; | |
| 362 | + | i: integer; | |
| 363 | + | begin | |
| 364 | + | wrapper := TIntArray.Create(100,200,300); // NOTE: we can't use array constructors [] due to bug in compiler | |
| 365 | + | wrapper[0] += 50; | |
| 366 | + | wrapper[1] += 1; | |
| 367 | + | for i in wrapper do | |
| 368 | + | writeln(i); | |
| 369 | + | end. | |
| 370 | + | ``` | |
| 371 | + | ||
| 372 | + | ### Delegation | |
| 373 | + | ||
| 374 | + | If delegate properties are allowed be multidimensional in the final version they can be used for delegation patterns that would otherwise only be possible with multiple inheritance (in languages such as C++, Java). | |
| 375 | + | ``` | |
| 376 | + | type | |
| 377 | + | TStats = record | |
| 378 | + | hp: integer; | |
| 379 | + | exp: integer; | |
| 380 | + | procedure InitStats; | |
| 381 | + | end; | |
| 382 | + | ||
| 383 | + | // Base Module: | |
| 384 | + | ||
| 385 | + | type | |
| 386 | + | TEntity = class; | |
| 387 | + | TBaseHandler = class | |
| 388 | + | entity: TEntity; | |
| 389 | + | constructor Create (inEntity: TEntity); | |
| 390 | + | procedure Clear; | |
| 391 | + | end; | |
| 392 | + | ||
| 393 | + | // Physics Module: | |
| 394 | + | ||
| 395 | + | TPhysicsHandler = class (TBaseHandler) | |
| 396 | + | pos: TVec3; | |
| 397 | + | acc: TVec3; | |
| 398 | + | vel: TVec3; | |
| 399 | + | procedure Integrate; virtual; | |
| 400 | + | end; | |
| 401 | + | ||
| 402 | + | // Renderer Module: | |
| 403 | + | ||
| 404 | + | TRendererHandler = class (TBaseHandler) | |
| 405 | + | procedure Render; virtual; | |
| 406 | + | end; | |
| 407 | + | ||
| 408 | + | // Entity: | |
| 409 | + | ||
| 410 | + | TEntity = class | |
| 411 | + | private | |
| 412 | + | m_physics: TPhysicsHandler; | |
| 413 | + | m_renderer: TRendererHandler; | |
| 414 | + | m_stats: TStats; | |
| 415 | + | public | |
| 416 | + | property physics: TPhysicsHandler read m_physics; default; | |
| 417 | + | property renderer: TRendererHandler read m_renderer; default; | |
| 418 | + | property stats: TStats read m_stats; default; | |
| 419 | + | public | |
| 420 | + | procedure Update; | |
| 421 | + | end; | |
| 422 | + | ||
| 423 | + | // Implemention: | |
| 424 | + | ||
| 425 | + | type | |
| 426 | + | TMonster = class (TEntity) | |
| 427 | + | public | |
| 428 | + | procedure AfterConstruction; override; | |
| 429 | + | end; | |
| 430 | + | ||
| 431 | + | type | |
| 432 | + | TMonsterRenderer = class (TRendererHandler) | |
| 433 | + | procedure Render; override; | |
| 434 | + | end; | |
| 435 | + | ||
| 436 | + | procedure TStats.InitStats; | |
| 437 | + | begin | |
| 438 | + | hp := 100; | |
| 439 | + | exp := 0; | |
| 440 | + | end; | |
| 441 | + | ||
| 442 | + | procedure TBaseHandler.Clear; | |
| 443 | + | begin | |
| 444 | + | writeln(classname,' clear'); | |
| 445 | + | end; | |
| 446 | + | ||
| 447 | + | constructor TBaseHandler.Create (inEntity: TEntity); | |
| 448 | + | begin | |
| 449 | + | entity := inEntity; | |
| 450 | + | end; | |
| 451 | + | ||
| 452 | + | procedure TPhysicsHandler.Integrate; | |
| 453 | + | begin | |
| 454 | + | pos.x += vel.x; | |
| 455 | + | pos.y += vel.y; | |
| 456 | + | pos.z += vel.z; | |
| 457 | + | end; | |
| 458 | + | ||
| 459 | + | procedure TRendererHandler.Render; | |
| 460 | + | begin | |
| 461 | + | end; | |
| 462 | + | RSS | |
| 463 | + | procedure TEntity.Update; | |
| 464 | + | begin | |
| 465 | + | Integrate; | |
| 466 | + | Render; | |
| 467 | + | end; | |
| 468 | + | ||
| 469 | + | procedure TMonster.AfterConstruction; | |
| 470 | + | begin | |
| 471 | + | m_physics := TPhysicsHandler.Create(self); | |
| 472 | + | m_renderer := TMonsterRenderer.Create(self); | |
| 473 | + | ||
| 474 | + | InitStats; | |
| 475 | + | ||
| 476 | + | vel.x := 80; | |
| 477 | + | vel.y := 20; | |
| 478 | + | vel.z := 0; | |
| 479 | + | end; | |
| 480 | + | ||
| 481 | + | procedure TMonsterRenderer.Render; | |
| 482 | + | begin | |
| 483 | + | writeln('render monster at ', entity.pos.str, ' hp:', entity.hp); | |
| 484 | + | end; | |
| 485 | + | ||
| 486 | + | var | |
| 487 | + | entity: TMonster; | |
| 488 | + | i: integer; | |
| 489 | + | begin | |
| 490 | + | entity := TMonster.Create; | |
| 491 | + | entity.Clear; | |
| 492 | + | for i := 0 to 2 do | |
| 493 | + | entity.Update; | |
| 494 | + | end. | |
| 495 | + | ``` | |
| 496 | + | ||
| 497 | + | ### Default "Implements" Properties | |
| 498 | + | ||
| 499 | + | "implements" properties are more complete by exposing their namespace using "default". If multiple defaults are allowed one could imagine this being a possible method to implement multiple inheritance in Pascal. | |
| 500 | + | ``` | |
| 501 | + | type | |
| 502 | + | IWrapper = interface ['IWrapper'] | |
| 503 | + | procedure DoThis; | |
| 504 | + | end; | |
| 505 | + | ||
| 506 | + | type | |
| 507 | + | TWrapper_Handler = class (IWrapper) | |
| 508 | + | procedure DoThis; | |
| 509 | + | end; | |
| 510 | + | ||
| 511 | + | procedure TWrapper_Handler.DoThis; | |
| 512 | + | begin | |
| 513 | + | writeln('TWrapper_Handler.DoThis'); | |
| 514 | + | end; | |
| 515 | + | ||
| 516 | + | type | |
| 517 | + | TWrapper = class (IWrapper) | |
| 518 | + | m_wrapper: TWrapper_Handler; | |
| 519 | + | property handler: TWrapper_Handler read m_wrapper implements IWrapper; default; | |
| 520 | + | procedure AfterConstruction; override; | |
| 521 | + | end; | |
| 522 | + | ||
| 523 | + | procedure TWrapper.AfterConstruction; | |
| 524 | + | begin | |
| 525 | + | m_wrapper := TWrapper_Handler.Create; | |
| 526 | + | end; | |
| 527 | + | ||
| 528 | + | procedure HandleWrapper (wrapper: IWrapper); | |
| 529 | + | begin | |
| 530 | + | wrapper.DoThis; | |
| 531 | + | end; | |
| 532 | + | ||
| 533 | + | var | |
| 534 | + | wrapper: TWrapper; | |
| 535 | + | begin | |
| 536 | + | wrapper := TWrapper.Create; | |
| 537 | + | HandleWrapper(wrapper); | |
| 538 | + | end. | |
| 539 | + | ``` | |
| 540 | + | ||
| 541 | + | ## COMPILER: Implemention Details | |
| 542 | + | ||
| 543 | + | Default properties are implemented primarily at the parser layer and overloads (proc and operator) are handled through tcallcandiates (must be explictly requested in create params). They could be handled in the node layer during typechecking instead if that was deemed more appropriate. I used the parser layer because regular properties are handled this way and I wasn't sure about the ramifications of making nodes convert themselves to properties in all instances across the entire platform. Furthermore I personally see default properties as a sort of "meta-layer" which doesn't really exist but is more of a re-routing scheme which is applied in advance of normal syntax. | |
| 544 | + | ||
| 545 | + | For reference here are some of the important changes made to support default properties. | |
| 546 | + | ||
| 547 | + | * symtable.pas | |
| 548 | + | ||
| 549 | + | searchsym_xxx() functions now contain parameters which explicitly request defaults to be included in the result (see ssf_search_defaults) and return a tpropertysym. searchsym_with_defaults() is added for unit level searching. | |
| 550 | + | ||
| 551 | + | * pexpr.pas | |
| 552 | + | ||
| 553 | + | ``` | |
| 554 | + | do_member_read_internal() - handles the default property sym returned by searchsym_xxx using handle_read_property(). | |
| 555 | + | do_proc_call() - after proc params are parsed default read property is handled via handle_default_proc_call(). | |
| 556 | + | ``` | |
| 557 | + | ||
| 558 | + | Other relevant functions: | |
| 559 | + | ``` | |
| 560 | + | handle_default_assignment() | |
| 561 | + | handle_default_proc_call() | |
| 562 | + | handle_default_unary_operator() | |
| 563 | + | handle_default_binary_operator() | |
| 564 | + | ``` | |
| 565 | + | ||
| 566 | + | * htypcheck.pas | |
| 567 | + | ||
| 568 | + | Procdefs returned from tcallcandidates (if requested in constructor) can return procdefs from default properties. To help facilitate this tprocoverloadentry class was added (see collect_overloads_in_struct). | |
| 569 | + | ||
| 570 | + | The new overload for choose_best will return a tpropertysym (if the the best procdef belongs to a default property) so it can be handled properly. | |
| 571 | + | ||
| 572 | + | * pstatmnt.pas | |
| 573 | + | ||
| 574 | + | Expressions of type torddef are converted to properties using handle_expr_default_property_access(). | |
| 575 | + | ||
| 576 | + | * nutils.pas | |
| 577 | + | ||
| 578 | + | Various helper functions for default properties: | |
| 579 | + | ``` | |
| 580 | + | handle_read_property() | |
| 581 | + | handle_write_property() | |
| 582 | + | try_default_read_property() | |
| 583 | + | ``` | |
| 584 | + | ||
| 585 | + | * nflw.pas | |
| 586 | + | ||
| 587 | + | Uses try_default_read_property() to resolve default read properties for for..in enumerators. This is only possible for default properties that support the for..in loop, such as arrays, sets etc... | |
| 588 | + | ||
| 589 | + | --- | |
| 590 | + | *From the Free Pascal wiki, page "default properties", as the Internet Archive kept it on 2022-12-30. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
