diff --git a/Cesium.CodeGen.Tests/CodeGenMethodTests.cs b/Cesium.CodeGen.Tests/CodeGenMethodTests.cs index ec4124aa..440338ee 100644 --- a/Cesium.CodeGen.Tests/CodeGenMethodTests.cs +++ b/Cesium.CodeGen.Tests/CodeGenMethodTests.cs @@ -342,14 +342,23 @@ public Task ImplicitReturnAllowedForMain() => DoTest(@"int main() int unused = 0; }"); - [Fact, NoVerify] - public void ImplicitReturnDisallowedNonMain() => DoesNotCompile(@"int foo() + [Fact] + public void ImplicitReturnAllowedNonMain() => DoTest(@"int foo() { int unused; -}", "Function foo has no return statement."); +}"); + + [Fact, NoVerify] + public void ExpressionReturnDisallowedInVoidFunction() => DoesNotCompile(@"void foo() +{ + return 4; +}", "Function foo has return type void, and thus cannot have expression in return."); + + [Fact] + public Task ReturnInsideInfiniteForTest() => DoTest("void test() { for(;;) { return; } }"); [Fact] - public Task ReturnInsideInfiniteForTest() => DoTest("int test() { for(;;) { return; } }"); + public Task ReturnValueInsideInfiniteForTest() => DoTest("int test() { for(;;) { return 5; } }"); [Fact] public Task ConsumeUnusedResult() => DoTest(@" diff --git a/Cesium.CodeGen.Tests/verified/CodeGenIfTests.IfElseTest.verified.txt b/Cesium.CodeGen.Tests/verified/CodeGenIfTests.IfElseTest.verified.txt index cd51c547..683ba99b 100644 --- a/Cesium.CodeGen.Tests/verified/CodeGenIfTests.IfElseTest.verified.txt +++ b/Cesium.CodeGen.Tests/verified/CodeGenIfTests.IfElseTest.verified.txt @@ -16,6 +16,8 @@ IL_0014: ldc.i4.0 IL_0015: ret IL_0016: nop + IL_0017: ldc.i4.0 + IL_0018: ret System.Int32 ::() Locals: diff --git a/Cesium.CodeGen.Tests/verified/CodeGenIfTests.IfElseWithNestedDeclarationTest.verified.txt b/Cesium.CodeGen.Tests/verified/CodeGenIfTests.IfElseWithNestedDeclarationTest.verified.txt index 17b9e803..da5e9669 100644 --- a/Cesium.CodeGen.Tests/verified/CodeGenIfTests.IfElseWithNestedDeclarationTest.verified.txt +++ b/Cesium.CodeGen.Tests/verified/CodeGenIfTests.IfElseWithNestedDeclarationTest.verified.txt @@ -15,6 +15,8 @@ IL_0012: ldc.i4.0 IL_0013: ret IL_0014: nop + IL_0015: ldc.i4.0 + IL_0016: ret System.Int32 ::() Locals: diff --git a/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.ImplicitReturnAllowedNonMain.verified.txt b/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.ImplicitReturnAllowedNonMain.verified.txt new file mode 100644 index 00000000..10e96865 --- /dev/null +++ b/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.ImplicitReturnAllowedNonMain.verified.txt @@ -0,0 +1,3 @@ +System.Int32 ::foo() + IL_0000: ldc.i4.0 + IL_0001: ret diff --git a/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.ReturnInsideInfiniteForTest.verified.txt b/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.ReturnInsideInfiniteForTest.verified.txt index bcd35cc6..1a2667a3 100644 --- a/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.ReturnInsideInfiniteForTest.verified.txt +++ b/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.ReturnInsideInfiniteForTest.verified.txt @@ -1,4 +1,4 @@ -System.Int32 ::test() +System.Void ::test() IL_0000: nop IL_0001: ret IL_0002: nop diff --git a/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.ReturnValueInsideInfiniteForTest.verified.txt b/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.ReturnValueInsideInfiniteForTest.verified.txt new file mode 100644 index 00000000..df5be62a --- /dev/null +++ b/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.ReturnValueInsideInfiniteForTest.verified.txt @@ -0,0 +1,9 @@ +System.Int32 ::test() + IL_0000: nop + IL_0001: ldc.i4.5 + IL_0002: ret + IL_0003: nop + IL_0004: br IL_0000 + IL_0009: nop + IL_000a: ldc.i4.0 + IL_000b: ret diff --git a/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.VariablesInsideNestedIf.verified.txt b/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.VariablesInsideNestedIf.verified.txt index 0225aad7..4b75f859 100644 --- a/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.VariablesInsideNestedIf.verified.txt +++ b/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.VariablesInsideNestedIf.verified.txt @@ -32,6 +32,8 @@ IL_002b: nop IL_002c: br IL_0006 IL_0031: nop + IL_0032: ldc.i4.0 + IL_0033: ret System.Int32 ::() Locals: diff --git a/Cesium.CodeGen/Ir/BlockItems/FunctionDefinition.cs b/Cesium.CodeGen/Ir/BlockItems/FunctionDefinition.cs index 0cbafe9c..057b0a6e 100644 --- a/Cesium.CodeGen/Ir/BlockItems/FunctionDefinition.cs +++ b/Cesium.CodeGen/Ir/BlockItems/FunctionDefinition.cs @@ -280,5 +280,11 @@ private void EmitCode(FunctionScope scope) ); BlockItemEmitting.EmitCode(scope, transformed); + var isVoid = scope.FunctionInfo.ReturnType.Equals(CTypeSystem.Void); + if (!isVoid && scope.Method.Body.Instructions.Last().OpCode != OpCodes.Ret) + { + scope.Method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4_0)); + scope.Method.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); + } } } diff --git a/Cesium.CodeGen/Ir/ControlFlow/ControlFlowChecker.cs b/Cesium.CodeGen/Ir/ControlFlow/ControlFlowChecker.cs index 4680863e..beea9f24 100644 --- a/Cesium.CodeGen/Ir/ControlFlow/ControlFlowChecker.cs +++ b/Cesium.CodeGen/Ir/ControlFlow/ControlFlowChecker.cs @@ -314,19 +314,22 @@ bool isMain var isVoidFn = returnType.Equals(CTypeSystem.Void); var isReturnRequired = !isVoidFn && !isMain; + if (isVoidFn) + { + var hasExpressionReturn = (ReturnStatement?)dset.Vertices.FirstOrDefault(_ => _.BlockItem is ReturnStatement { Expression: { } })?.BlockItem; + if (hasExpressionReturn is not null) + { + throw new CompilationException($"Function {scope.Method.Name} has return type void, and thus cannot have expression in return."); + } + } + foreach (var preTerminator in preTerminators) { if (preTerminator.BlockItem is ReturnStatement) continue; if (preTerminator.Reached == false) continue; - if (isReturnRequired) - { - // bad error message - throw new CompilationException($"Function {scope.Method.Name} has no return statement."); - } - // inserting fake return - var retn = new ReturnStatement(isMain ? new ConstantLiteralExpression(new IntegerConstant(0)) : null); + var retn = new ReturnStatement(!isVoidFn ? new ConstantLiteralExpression(new IntegerConstant(0)) : null); if (preTerminator.BlockItem is {} original) { diff --git a/Cesium.IntegrationTests/functions.c b/Cesium.IntegrationTests/functions.c index f4ca4381..a87c95c1 100644 --- a/Cesium.IntegrationTests/functions.c +++ b/Cesium.IntegrationTests/functions.c @@ -23,6 +23,11 @@ void forward_declaration_void_2() // Do nothing here. } +int missing_return() +{ + // Do nothing here. +} + int main(void) { return foo(); diff --git a/Cesium.IntegrationTests/if.c b/Cesium.IntegrationTests/if.c new file mode 100644 index 00000000..874d00d0 --- /dev/null +++ b/Cesium.IntegrationTests/if.c @@ -0,0 +1,17 @@ +#include + +int test() +{ + int a = 0; + if (1) + return 3; + else + a = 2; +} + +int main(int argc, char *argv[]) +{ + if (test() != 3) return -1; + + return 42; +}