Difference between revisions of "User Changes Trunk"

From Lazarus wiki
Jump to navigationJump to search
m
(Next fixes release in 2.2.6, if ever released.)
Line 1: Line 1:
 
== About this page==
 
== About this page==
  
Below you can find a list of intentional changes since the [[User_Changes_2.2.4|the previous release]] not yet scheduled for inclusion in the next [[User_Changes_2.2.2|fixes release]] which can change the behaviour of previously working code, along with why these changes were performed and how you can adapt your code if you are affected by them.
+
Below you can find a list of intentional changes since the [[User_Changes_2.2.4|the previous release]] not yet scheduled for inclusion in the next [[User_Changes_2.2.6|fixes release]] which can change the behaviour of previously working code, along with why these changes were performed and how you can adapt your code if you are affected by them.
  
 
== All systems ==
 
== All systems ==

Revision as of 10:25, 3 February 2009

About this page

Below you can find a list of intentional changes since the the previous release not yet scheduled for inclusion in the next fixes release which can change the behaviour of previously working code, along with why these changes were performed and how you can adapt your code if you are affected by them.

All systems

Alignment of record variables

  • Old behaviour: Variables of record types (not just their fields, but the records as a whole when declared as independent variables) would be aligned at most to the maximum alignment of their fields, limited by the maximum field alignment set for the target. This same limit was used to determine padding of the record size.
  • New behaviour: Variables of record types are now always aligned inside stack frames or as global variables in a way which provides optimal alignment for their embedded fields, regardless of the used packrecords setting. Moreover, unpacked records are also padded up to a size which is a multiple of this alignment (to also provide optimal alignment inside arrays of such records). The alignment of record fields inside other records obviously only depends on the packing settings of the "parent" record.
  • Example:

<delphi> type

 tr = packed record
   d: double;
   b: byte;
 end;

</delphi> tr used to be aligned to 1 byte in stack frames and as global variable. Now it will be aligned to the native alignment of double (4 or 8 bytes depending on the target platform, limited by the maximum global/local alignment settings). Its size will remain 9 bytes as before (because of the packed specifier).

  • Reason: Performance.
  • Effect: The size of some non-packed records may change compared to previous versions. Other than that, the different alignment rules cannot impact your code unless you are making unsupported assumptions (like taking the address of a local variable, adding some value to it, and expecting that you are now pointing at the next local variable).
  • Remedy: If you depend on the layout and/or size of a record staying the same, always declare it as packed. Non-packed records are free to be changed by the compiler in any way that it sees fit (except for changing the types or order of the fields).

Byte/Word/Long/Qwordbool types

  • Old behaviour: Assigning "true" to variables of these types resulted in these variables getting the value "1". Typecasting ordinal values to Byte/Word/Long/Qwordbool also mapped these values onto [0,1] if the source and destination type were of different sizes.
  • New behaviour: Assigning true to such variables now sets them to "-1" (i.e., all 1 bits). Typecasting an ordinal value to such a type now leaves that ordinal value untouched.
  • Example:

<delphi> var

 b: byte;
 bb: bytebool;

begin

 bb:=true;
 writeln(ord(bb));
 b:=3;
 writeln(byte(wordbool(b)));

end. </delphi> This program used to print 1 in both cases, now it prints -1 for the first statement and 3 for the second.

  • Reason: Delphi-compatibility, compatibility with WinAPI functions. See http://bugs.freepascal.org/view.php?id=10233 for more information.
  • Effect: Code assuming that assigning true to a Byte/Word/Long/Qwordbool variable or parameter results in assigning the ordinal value "1" no longer works. This may affect e.g. translations of C headers where certain int parameters which function basically as boolean parameters were replaced with longbool.
  • Remedy: If you depend on the the ordinal value of a particular variable being 1, either use an expression such as longbool(1) (which can also be used in the declaration of a constant), or use a regular ordinal type rather than one of the *bool types.

Passing ordinal constants to formal const parameters

  • Old behaviour: Ordinal constants could be passed directly to formal parameters.
  • New behaviour: Passing ordinal constants to formal parameters is no longer allowed.
  • Example:

<delphi> procedure test(const a); begin end;

begin

 test(5);

end. </delphi> The above program used to compile, but now it does not anymore.

  • Reason: It is not clear from the code above what the size of the ordinal constant (1, 2, 4 or 8 bytes) will be when it is passed to the procedure. See for example http://bugs.freepascal.org/view.php?id=9015 for how this can cause problems. The change in behaviour is also Delphi-compatible.
  • Effect: Code using this construct will no longer compile.
  • Remedy: Declare a variable of the appropriate size, assign the value to it and then pass this variable as parameter instead.

Treating direct-mapped properties as regular fields

  • Old behaviour: The compiler allowed treating properties that directly read and write a field (as opposed to redirecting via a getter/setter) as direct references to this field. This means that you could pass such properties to var and out parameters, that you could take their address, and that you could assign values to subscripted properties with non-pointer result types (see example //2 below).
  • New behaviour: All properties are now treated equal, regardless of whether they directly map to a field or use a getter/setter.
  • Example:

<delphi> {$mode objfpc} type

 trec = record
   a, b: integer;
 end;
 tc = class
  private
   fmyfield: integer;
   frec: trec;
  public
   property myfield: integer read fmyfield write fmyfield;
   property rec: trec read frec write frec;
 end;

var

 c: tc;

begin

 c:=tc.create;
 inc(c.myfield); //1
 c.rec.a:=5; //2

end. </delphi> The above code used to compile. Now the line marked with //1 will be flagged as Can't take the address of constant expressions, and the line marked with //2 as Argument can't be assigned to.

  • Reason: Properties abstract the underlying memory layout and class implementation. By ignoring this abstraction in case a property directly mapped to a field, it became impossible to afterwards transparently change the property into an indirection via a getter and/or setter. The new behaviour is also Delphi-compatible.
  • Remedy: Change your code so that the address of properties is no longer taken, that they are no longer used as var or out parameters, and that subscripts of properties with a non-pointer result types are no longer assigned to. Note that a class instance qualifies as a pointer type in this context.

Encoding of single-character constants assigned to widestring

  • Old behaviour: If a source file's encoding was not utf-8 and a single character constant was assigned directly to a widestring, then this character would not be converted from the source code's code page. It would therefore result in a string with ord(str[1]) equal to the ordinal value of that character as it appeared in the source file (i.e., in the source file's code page).
  • New behaviour: Such character constants are now converted at compile time from the source file's code page into an utf-16 character before being stored into the widestring. In case of ansistrings/shortstrings, nothing changes (i.e., a character with the original ordinal value as it appears in the source file is directly stored into the ansistring/shortstring, without any compile-time conversion)
  • Example:

<delphi> {$codepage cp866}

{$ifdef unix} uses

 cwstring;

{$endif}

var

 w: widestring;
 a: ansistring;

begin

 w:='Б';
 s:='Б';

end. </delphi> (it is assumed that the above source file is saved using code page 866)

The "Б" character has ordinal value 129 in code page 866. Previously, at run time ord(w[1]) and ord(s[1]) would equal 129. Now, at run time w[1] will equal widechar('Б'), and s[1] will (still) equal #129.

  • Reason: The previous behaviour was buggy, as multi-character constants were correctly converted from the source file's code page.
  • Remedy: If you want to store a particular ordinal value directly into a widestring without the compiler converting anything regardless of the source file's code page, use #xyza notation (i.e., use 4 digits to define the number, e.g. #0129 in the above example).

Overloading the assignment operator with a shortstring result

  • Old behaviour: It was possible to overload the assignment operator ":=" for every possible shortstring length as result (i.e., for string[1], string[2], ..., string[255])
  • New behaviour: It is now only possible to overload the assignment operator for string[255] as result.
  • Example:

<delphi> type

 ts1 = string[4];
 ts2 = string[255];

operator :=(l: longint) res: ts1; begin

 str(l:4,res);

end;

operator :=(l: longint) res: ts2; begin

 str(l:20,res);

end;

begin end. </delphi> The above code used to compile in previous versions. Now, the operator with ts1 as result is refused.

  • Reason: Since shortstrings of all lengths are assignment-compatible with all other shortstrings, an overloaded assignment operator defined for a single shortstring length has to work for assigning to shortstrings of all lengths (see bug #12109). As a result, having assignment operators for multiple shortstring lengths would introduce ambiguity in case there is no exact match.
  • Remedy: If you require differentiating between multiple shortstring lengths, you now have to wrap these shortstrings in a record:

<delphi> type

 ts1 = record
   s: string[4];
 end;
 ts2 = record
   s: string[255];
 end;

operator :=(l: longint) res: ts1; begin

 str(l:4,res.s);

end;

operator :=(l: longint) res: ts2; begin

 str(l:20,res.s);

end;

begin end. </delphi>