Oxygene/C# mixed code

Hi there,
we have discovored a strange compiler behavior when mixing C# and pascal code in the same project.
Could someone advice us what is wrong here ?

There is an example VS solution TC.sln within the atatchments, please see files ClassA.pas and ClassA.cs inside the project DeclarationConstants.elements.

Sources are not compilable, compiler produces erros:

  • ClassA.pas(16,9): error E464: Case for identifier “string” does not match original case “String”
    Should Case mistmatch be an error in pascal with <WarnOnCaseMismatch>True</WarnOnCaseMismatch> ?
  • ClassA.pas(19,12): error E633: Cannot invoke type “ClassA”, did you mean to use new?
    ClassA(obj) shoud be a typecast syntax.
  • ClassA.pas(20,5): error E513: Parenthesis are required to call method Apply()
    Are parentheses mandatory while calling parameterless method ?

These errors appear only when the Apply method header is declared in C#, when it is declared in Pascal all look fine. Looks like the C# method header declaration turns on a more strict compiler mode.

Thanks for support

There are more projects with more questions inside the solution, consider them as offtopic. Ill ask them in other thread.
TC.zip (42.3 KB)

I get

   -> Task RemObjects.EBuild.BuildSolution started.
E:    Project 'DefineConstantTC' has the same Project ID as project 'DirectiveTC'. [/Users/mh/Downloads/TC/DefineConstantTC/DefineConstantTC.elements]
E:    Project 'GlobalUsingsTC' has the same Project ID as project 'DefineConstantTC'. [/Users/mh/Downloads/TC/GlobalUsingsTC/GlobalUsingsTC.elements]
E:    Project 'GlobalTypeAliasTC' has the same Project ID as project 'GlobalUsingsTC'. [/Users/mh/Downloads/TC/GlobalTypeAliasTC/GlobalTypeAliasTC.elements]
   <- Task RemObjects.EBuild.BuildSolution failed for TC, took 0.0347s (0.036s).

when io try to build your solution. Not sure why you dont, but you should always make sure that every project has a unique ID.

Fixing this I get

TC: PreProcessorDirectivesTC.cs, line 4 — (E1) opening brace expected, got 
TC: PreProcessorDirectivesTC.cs, line 6 — (E1) opening brace expected, got 
TC: GlobalUsings.cs, line 5 — (E1) ; expected, got using
TC: GlobalUsings.cs, line 5 — (E46) Unknown identifier "global"
TC: ClassB.cs, line 4 — (E28) Unknown type "GlobalTypeAlias"

but they all seem legit errors.

OK, your result is completely different, why ?
Im building the solution in Visual Studio 2022 - there is no problem with Projects duplicate IDs.
Since you dont have any errors in ClassA, i guess there is something wrong in Visual Studio / MSBuild integration which probably works fine in your Water IDE ?

VS/MSBuild doesn’t check this (but might run into problems later). because dupe project IDs can cause many problems with project references, EBuild will warn/fail on them.

Do you get the same errors in Water? VS and Water use the same compiler, so i’d not normally expect different code-level errors or behavior between the two IDEs.

I can see tha same errors in Water:

"C:\Program Files (x86)\RemObjects Software\Elements\Bin\EBuild.exe" --logger:fire C:\Users\tumal\Downloads\ElementsTC\ElementsTC\TC.sln --configuration:Debug --build --setting:TreatFixableErrorsAsWarnings=True --xml:C:\Users\tumal\Downloads\ElementsTC\ElementsTC\obj\TC.water.xml --no-goal --verbosity:normal
RemObjects EBuild. An open source build engine for Elements and beyond.
Copyright RemObjects Software 2016-2023. All Rights Reserved. Created by marc hoffman.
Version 11.0.0.2833 (develop) built on bajor, 20230421-150719. Commit 0864568.

W: Method definition "method Test(): object" does not exactly match implementation "function ClassA.Test(): Object" [C:\Users\tumal\Downloads\ElementsTC\ElementsTC\ConstrucotrConflictTC\ClassA.pas (12)]
E: Case for identifier "string" does not match original case "String" [C:\Users\tumal\Downloads\ElementsTC\ElementsTC\ConstrucotrConflictTC\ClassA.pas (16)]
E: Cannot invoke type "ClassA", did you mean to use new? [C:\Users\tumal\Downloads\ElementsTC\ElementsTC\ConstrucotrConflictTC\ClassA.pas (19)]
E: Parenthesis are required to call method Apply() [C:\Users\tumal\Downloads\ElementsTC\ElementsTC\ConstrucotrConflictTC\ClassA.pas (20)]

There is an updated example solution. IDs and project Names fixed.
TC.zip (54.9 KB)

this one reproduces your errors, but they still all seem legit:

unit Test;
interface
type

ClassA = public partial class
    //public function Test() : Object; //todo: commnent this to fix compile errors
end;

implementation
uses Test;

function ClassA.Test() : Object; // W66 Method definition "method Test(): object" does not exactly match implementation "function ClassA.Test(): Object"
var
  bar : Object;
  foo  : ClassA;
  str : String;

begin;
    foo := ClassA(bar); // E633 Cannot invoke type "ClassA", did you mean to use new?
    foo.Apply; // E513 Parenthesis are required to call method Apply()

    result := foo;
end;

end.
  • there’s no declaration for test(), its commented out, so the W66 is expected. if I add it I getb a “duplicate method” which also makes sense because the other partial declares
        public object Test(); //todo: commnent this to fix compile errors

and you cant have two methods of the same name. If I rename it to test2, its fine and all related errors go away. So this is as designed and expected.

next

#if true
public class TestCase // E1 opening brace expected, got 
#else
public class TestCase // E1 opening brace expected, got 
#endif
{}

this is expected as well, as the #IF/#endif needs to either be inside or outside the class and surround it. in Elememts, you cannot ifdef arbitrary partial pieces of code, as the #if’s become part of the code tree.

next

    public class ClassB : GlobalTypeAlias // E28 Unknown type "GlobalTypeAlias"
    {
    }

is also expected, as GlobalTypeAlias does snot seem to be defined anywhere else in the project.

Finally


#if true
global using System;
#endif
global using System.Text; // E1 ; expected, got using
						  // E46 Unknown identifier "global"

this one does seem like a legit bug, assuming “global using” is a thing; I’m not familiar with that syntax. I’ll log a bug for it.

Update: it seems that “global using” either needs “static” or an “x = y” alias, according to this. But I agree it should emit better errors.

bugs://E26578

There is a declaration for test() in partial class ClassA.cs

This is very limiting. This is normally posible in C# and MSB/Roslyn compilation. The class declaration is the typical thing you need to differ, not the whole content.

It is declared in file GlobalTypeAlias.cs

According to that and to the author specs, it should work. Static is not mandatory, not an “x=y” .

Yes. hence the duplicate.

This is because Elements’ conditional compilation system is more sophisticated, and the compiler holds the model of all ifdef paths in a single syntax tree. requiring the syntax to be valid is one side effect of that. It can cause a bit of inconvenience once in a while, but it’s what it is.

    public class ClassB : GlobalTypeAlias
    {
    }
global using GlobalTypeAliasFor = Test.ClassA;

no it’s not. :wink:

But I do not complain about duplicate, you created the duplicate by yourself by uncommenting other code proving there is a bug. The problems reported here was:

  • ClassA.pas(19,12): error E633: Cannot invoke type “ClassA”, did you mean to use new?
    ClassA(obj) shoud be a typecast syntax.
  • ClassA.pas(20,5): error E513: Parenthesis are required to call method Apply()
    Are parentheses mandatory while calling parameterless method ?

And those errors appear only when the method declaration is in C#, but the implementation in pascal…

(Third bug - ClassA.pas(16,9): error E464: Case for identifier “string” does not match original case “String”) - just dissapeared at my side. Something else caused it, mabye the project ID duplicates.)

Noted, my team tries to find other structure to workaround that weakness.

That is a Typo in the example code, sorry fort that. The point here is that even if the using name matches, its not found either. Fixed TC here:
TC.zip (54.9 KB)

One way to solve this problem is to change the string length setting in your code. Instead of using the Size value to set the string length in Unicode Delphi from UnicodeString, you need to use the appropriate length given the character size.
Here’s an example of how this might be implemented:


If (l_status >= 300) and (l_status <> HTTP_500_code) then begin
l_index := 0;
l_size := const_MaxStatusText;
SetLength(l_statusText, l_size div SizeOf(WideChar)); // Take into account the character size
if HTTPQueryInfoW(aRequest, HTTP_QUERY_STATUS_TEXT, @l_statusText[1], l_size, l_index) then begin
raise EROWinInetHttpException.CreateFmt(‘%s (%d)’, [l_statusText, l_status], GetLastError);
end;
end;

In this example, we use a division by SizeOf(WideChar) when setting the string length of l_statusText. This ensures the correct number of characters, given the size of the character in Delphi’s Unicode.
This change should help avoid problems with incorrect characters and ensure that your code works correctly in Delphi 11.2 and other versions of Unicode Delphi.

Yes, but your test case, as is ALSO gives you this first error:

ClassA = public partial class
    //public function Test() : Object; //todo: commnent this to fix compile errors
end;

implementation
uses Test;

function ClassA.Test() : Object; // W66 Method definition "method Test(): object" does not exactly match implementation "function ClassA.Test(): Object"

// W66 Method definition “method Test(): object” does not exactly match implementation “function ClassA.Test(): Object”

which is legit. until you comment the declaration back in, you cannot have an implementation!? After that all bets are off in that method body, and the rest are follow-ups.

repro’d logging.

Logged as bugs://E26580.

bugs://E26578 was closed as fixed.

bugs://E26580 was closed as fixed.

Sorry, but i still dont get it: How is related W66 (WARNING) with the errors (E633,E513)? And how does the declaration and implementation differ ? Only by the language.

From here it looks like not posiible declaring method in C# and implement it in pascal. Do you say that this is a legit limitation ?

Thanks for the global using fixes, looking forward to the containing build.

there IS no declaration, because it is commented out.

Oxygene works in two modes (which can be mixed): a method can have a declration-only in the class body, and a separate implementation outside, in the “implementation” section of the same file, or it can have the full method in the class body (the new “unified class syntax”). what you cannot do is have just an implementation.

The compiler sees

function ClassA.Test() : Object; // W66 Method definition "method Test(): object" does not exactly match implementation "function ClassA.Test(): Object"

in the implementation, says “I don’t know of this method because it was not in the declaration up top in the interface section”, and then all bets are off.

(we could argue that it could fail better, and not emit these extra messages (and I’ll log an issue for that), but that doesn’t change the fact that this is just broken code, and that is what is causing then follow-ups).

I’m not sure what you are trying to achieve here, or why it it important that these errors happen, since they go away once you fix the real problem.

DO NOTE that the separate

public object Test();

declared in the C# part does not qualify as a declaration for this. That’s just a separate empty (and in this case, duplicate, because Oxygene is not case sensitive) method.

C# has no concept of declaring a method to implement later (and even if it did, declaration and implementation have to be in the same file).

Onde again I iterate that i’m unsure what you are trying to achieve with this code.

marc

Logged as bugs://E26582.

bugs://E26582 was closed as fixed.

1 Like