Compiler Code Optimization causes Access violation

Hi Forum,
I discovered a weird error and it took me hours to find it, but I don?t have a solution so far. Maybe do you see this before.
 
I have a code like:
 
   PersonList := FRepository.GetPersonList(MyKunde, 0, '');
   if PersonList.Count>0 then
     MyPerson := PersonList.Items[0];
   if MyPerson = nil then  
   begin
     WriteLog('PDFLetter.CreateRechnung: Error: Person not found!');
     Exit;
   end;
   MyAdresse := Person.Adresse; // << here OR
   MyAdresse := FRepository.GetAdresse(Person.Adresse.uid); // << same here
 
If I compile in debug mode without code optimization it runs without any problem, but when I run it in release mode with the code optimization switch on, I get a access violation during runtime in the last line. 
 
The access to the object in Person.Adresse is the reason for the problem which I found out with logging. No matter how I try to access the Person.Adresse object I get this error:
 
Exception in class $C0000005 with message 'access violation at 0x01329c4c: read of address 0x00000005' 
 
Very weird is that I can access the Person.Kunde object with no problem in both modes, which is very similar and all options are identical:
 
The relation between Person and Address is a 1:n bound with a TGuid field.
Adresse.uid = Person.Adresse with no action for delete and update.
 
The association fetch mode is set to ?Lazy? and the cascade type is ?None?.
 
I use Delphi Seattle 10 with FireDAC on MSSQL DB. This behaviour is with Aurelius 2.9 and 3.0 the same. I just updated it.
 
Why? Any ideas?
 

here the details from the resulting Entities.pas:

    [Association([TAssociationProp.Lazy], [])]
    [JoinColumn('Kunde', [], 'uid')]
    FKunde: Proxy<TKunde>;
    
    [Association([TAssociationProp.Lazy], [])]
    [JoinColumn('Adresse', [], 'uid')]
    FAdresse: Proxy<TAdresse>;
    function GetKunde: TKunde;
    procedure SetKunde(const Value: TKunde);
    function GetAdresse: TAdresse;
    procedure SetAdresse(const Value: TAdresse);

Is this code 100% correct? Because you are setting a variable MyPerson and then trying to read a variable named Person. Just from the code you posted, the reason is just because Person variable isn't initialized.


Hmm - yes of course you're right, it was my fault but only in this example.


Unfortunately the code is correctly trying to access the MyPerson object and results in a access violation in release mode.

Here is my correct code:

  if Rechnung.Person<>nil then
    MyPerson := Rechnung.Person;
  if MyPerson = nil then
  begin
    PersonList := GetPersonList(Kunde, 1, Typ);
    if (PersonList.Count<>0) then  
      MyPerson:= GetPerson(PersonList.Items[0].uid);
  end;
  if MyPerson = nil then
  begin
    WriteLog('PDFLetter.CreateRechnung: Error: No Person found!');
    Exit;
  end;
  WriteLog('PDFLetter.CreateRechnung: Person OK!');
  MyKunde := MyPerson.Kunde;      // this works fine
  MyAdresse := MyPerson.Adresse;  // access violation
  uid_MyAdresse := MyPerson.Adresse.uid;  // access violation
  WriteLog('PDFLetter.CreateRechnung: Person.Adresse OK!');


Have you destroyed the object manager you used to retrieve the list of Person? Does GetPersonList creates and destroys the manager? If that's the case it will fail, when lazy-loading associations, the original manager needs to stay alive so it can perform the remaining fetch actions (SQL statements) to retrieve the associated data.

No I don't think so:


function TDatabaseRepository.GetPersonList(Kunde : TKunde; Hauptkontakt : integer; PersonTyp : string): TList<TPerson>;
var
  FCriteria: TCriteria<TPerson>;
  ResultList : TList<TPerson>;
begin
  ResultList := TList<TPerson>.Create;
  if Kunde<>nil then
  begin
    // Mit PersonTyp
    if PersonTyp<>'' then
    begin
      if HauptKontakt = 0 then
        FCriteria := Manager.CreateCriteria<TPerson>.Where(TLinq.Eq('Kunde',Kunde.uid.ToString) AND TLinq.Eq('PersonTyp',PersonTyp) AND TLinq.Eq('Hauptkontakt',False)).OrderBy('Nachname',False).OrderBy('Vorname')
      else
      if HauptKontakt = 1 then
        FCriteria := Manager.CreateCriteria<TPerson>.Where(TLinq.Eq('Kunde',Kunde.uid.ToString) AND TLinq.Eq('PersonTyp',PersonTyp) AND TLinq.Eq('Hauptkontakt',True)).OrderBy('Nachname',False).OrderBy('Vorname')
      else
        FCriteria := Manager.CreateCriteria<TPerson>.Where(TLinq.Eq('Kunde',Kunde.uid.ToString) AND TLinq.Eq('PersonTyp',PersonTyp) ).OrderBy('Nachname',False).OrderBy('Vorname');
    end
    else
    begin
      // Ohne PersonTyp
      if HauptKontakt = 0 then
        FCriteria := Manager.CreateCriteria<TPerson>.Where(TLinq.Eq('Kunde',Kunde.uid.ToString) AND TLinq.Eq('Hauptkontakt',False)).OrderBy('Nachname',False).OrderBy('Vorname')
      else
      if HauptKontakt = 1 then
        FCriteria := Manager.CreateCriteria<TPerson>.Where(TLinq.Eq('Kunde',Kunde.uid.ToString) AND TLinq.Eq('Hauptkontakt',True)).OrderBy('Nachname',False).OrderBy('Vorname')
      else
        FCriteria := Manager.CreateCriteria<TPerson>.Where(TLinq.Eq('Kunde',Kunde.uid.ToString)).OrderBy('Nachname',False).OrderBy('Vorname');

    end;
    ResultList := FCriteria.List;
  end;
  Result := ResultList;
end;

if MyPerson.Adresse.uid, maybe Adresse is coming nil?

Sorry, but all this code you are posting is trivial (from what I can see) and is tested many times by different tests we have here, even indirectly. So if it's not something very simple like MyPerson.Adresse coming nil, it must be something very specific with your code that is not visible at first sight.
In this case I'd suggest you to build a sample project that can be compiled and run on our side here, so we can properly debug and fix the problem if it's a bug, or give you some guiding if the problem is in your code.

I was afraid that the reason for that would not be easy to find.


For the time being I can switch off the optimization for the release builds, which is not very nice but possible.

Thank you for your suggestion, I will see what I can do to make it reproduce able.