diff --git a/com.minres.coredsl.tests/src/com/minres/coredsl/tests/CoreDslTestCase.xtend b/com.minres.coredsl.tests/src/com/minres/coredsl/tests/CoreDslTestCase.xtend index 60086bbf..3113b843 100644 --- a/com.minres.coredsl.tests/src/com/minres/coredsl/tests/CoreDslTestCase.xtend +++ b/com.minres.coredsl.tests/src/com/minres/coredsl/tests/CoreDslTestCase.xtend @@ -24,6 +24,8 @@ import org.eclipse.xtext.validation.Issue.IssueImpl import static org.junit.jupiter.api.Assertions.* import static extension com.minres.coredsl.util.ModelExtensions.* +import com.minres.coredsl.coreDsl.Expression +import com.minres.coredsl.analysis.CoreDslConstantExpressionEvaluator class CoreDslTestCase { final static Pattern newLinePattern = Pattern.compile("\r?\n"); @@ -36,6 +38,7 @@ class CoreDslTestCase { final List expectedIssues = new ArrayList(); final List semanticExpectations = new ArrayList(); + boolean forceEvaluateExpressions; boolean checkDiagnosticsOnly; boolean isGenericSyntaxTest; boolean hasRun; @@ -59,6 +62,11 @@ class CoreDslTestCase { } this.prologLines = prologLines; } + + def evaluateExpressions() { + forceEvaluateExpressions = true; + return this; + } def diagnosticsOnly() { checkDiagnosticsOnly = true; @@ -237,6 +245,16 @@ class CoreDslTestCase { if(!checkDiagnosticsOnly) { val sink = new ValidationMessageSink(); results = CoreDslAnalyzer.analyze(model, sink); + + if(forceEvaluateExpressions) { + for(isa : results.results.keySet) { + val ctx = results.results.get(isa); + for(expr : isa.descendantsOfType(Expression)) { + CoreDslConstantExpressionEvaluator.evaluate(ctx, expr); + } + } + } + issues.addAll(sink.issues); } diff --git a/com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslExpressionTest.xtend b/com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslExpressionTest.xtend index 2741ded1..080566a9 100644 --- a/com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslExpressionTest.xtend +++ b/com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslExpressionTest.xtend @@ -18,6 +18,11 @@ import org.junit.jupiter.api.^extension.ExtendWith import static extension com.minres.coredsl.util.DataExtensions.* import static extension com.minres.coredsl.util.ModelExtensions.* +import com.minres.coredsl.coreDsl.DescriptionContent +import com.minres.coredsl.coreDsl.FunctionCallExpression +import com.minres.coredsl.coreDsl.IndexAccessExpression +import com.minres.coredsl.type.ErrorType +import com.minres.coredsl.type.ArrayType @ExtendWith(InjectionExtension) @InjectWith(CoreDslInjectorProvider) @@ -34,6 +39,15 @@ class CoreDslExpressionTest { ] } + def (DescriptionContent) => EObject progInitializerOf(String name) { + return [ + it.descendantsOfType(Declarator) + .findFirst[it.name == name] + ?.initializer.castOrNull(ExpressionInitializer) + ?.value + ] + } + @Test def booleanLiterals() { ''' @@ -423,6 +437,876 @@ class CoreDslExpressionTest { .expectError(IssueCodes.UnsupportedSizeOfDuringElaboration, 4) .run(); } + + @Test + def castExpression() { + ''' + long a = (int)0; + long b = (int)0x100000000; + ''' + .testStatements() + .expectType(null, initializerOf('a'), IntegerType.signed(32)) + .expectType(null, initializerOf('b'), IntegerType.signed(32)) + .run(); + + ''' + long a = (unsigned int)0; + long b = (unsigned int)0x100000000; + ''' + .testStatements() + .expectType(null, initializerOf('a'), IntegerType.unsigned(32)) + .expectType(null, initializerOf('b'), IntegerType.unsigned(32)) + .run(); + + ''' + long a = (signed<14>)0; + long b = (unsigned<14>)0; + ''' + .testStatements() + .expectType(null, initializerOf('a'), IntegerType.signed(14)) + .expectType(null, initializerOf('b'), IntegerType.unsigned(14)) + .run(); + + ''' + long a = (signed)(unsigned int)0; + long b = (unsigned)(signed int)0; + ''' + .testStatements() + .expectType(null, initializerOf('a'), IntegerType.signed(32)) + .expectType(null, initializerOf('b'), IntegerType.unsigned(32)) + .run(); + + + ''' + long a = (signed)(signed int)0; + long b = (unsigned)(unsigned int)0; + long c = (signed int)(signed int)0; + long d = (unsigned int)(unsigned int)0; + ''' + .testStatements() + .expectType(null, initializerOf('a'), IntegerType.signed(32)) + .expectType(null, initializerOf('b'), IntegerType.unsigned(32)) + .expectType(null, initializerOf('c'), IntegerType.signed(32)) + .expectType(null, initializerOf('d'), IntegerType.unsigned(32)) + .expectWarning(IssueCodes.IdentityCast, 1) + .expectWarning(IssueCodes.IdentityCast, 2) + .expectWarning(IssueCodes.IdentityCast, 3) + .expectWarning(IssueCodes.IdentityCast, 4) + .run(); + } + + @Test + def concatenationExpression() { + ''' + unsigned<1> u1; + unsigned<2> u2; + unsigned<4> u4; + signed<1> s1; + signed<2> s2; + signed<4> s4; + + long a = u1 :: u2 :: u4; + long b = s1 :: s2 :: s4; + ''' + .testStatements() + .expectType(null, initializerOf('a'), IntegerType.unsigned(7)) + .expectType(null, initializerOf('b'), IntegerType.unsigned(7)) + .run(); + } + + @Test + def conditionalExpression() { + ''' + InstructionSet TestISA { + architectural_state { + struct T { + int f; + } + } + functions { + void testFunc() { + struct T t; + int a[1]; + + bool b1 = t ? 1 : 0; + bool b2 = a ? 1 : 0; + } + } + } + ''' + .testProgram() + .expectError(IssueCodes.NonScalarCondition, 12) + .expectError(IssueCodes.NonScalarCondition, 13) + .run(); + + ''' + unsigned<16> u16; + unsigned<32> u32; + signed<16> s16; + signed<32> s32; + + long a = true ? u32 : u32; + long b = true ? s32 : s32; + long c = true ? u16 : u32; + long d = true ? u32 : u16; + long e = true ? s16 : s32; + long f = true ? s32 : s16; + + long g = true ? s32 : u16; + long h = true ? u16 : s32; + + long i = true ? s16 : u32; + long j = true ? u32 : s16; + ''' + .testStatements() + .expectType(null, initializerOf('a'), IntegerType.unsigned(32)) + .expectType(null, initializerOf('b'), IntegerType.signed(32)) + .expectType(null, initializerOf('c'), IntegerType.unsigned(32)) + .expectType(null, initializerOf('d'), IntegerType.unsigned(32)) + .expectType(null, initializerOf('e'), IntegerType.signed(32)) + .expectType(null, initializerOf('f'), IntegerType.signed(32)) + .expectType(null, initializerOf('g'), IntegerType.signed(32)) + .expectType(null, initializerOf('h'), IntegerType.signed(32)) + .expectType(null, initializerOf('i'), IntegerType.signed(33)) + .expectType(null, initializerOf('j'), IntegerType.signed(33)) + .run(); + + ''' + InstructionSet TestISA { + architectural_state { + struct T { + int f; + } + } + functions { + void testFunc() { + struct T t; + int a[1]; + + struct T t1 = true ? t : t; + struct T t2 = true ? t : 0; + struct T t3 = true ? 0 : t; + + int a1[1] = true ? a : a; + int a2[1] = true ? a : 0; + int a3[1] = true ? 0 : a; + } + } + } + ''' + .testProgram() + .expectError(IssueCodes.IncompatibleOptionTypes, 13) + .expectError(IssueCodes.IncompatibleOptionTypes, 14) + .expectError(IssueCodes.IncompatibleOptionTypes, 17) + .expectError(IssueCodes.IncompatibleOptionTypes, 18) + .run(); + } + + @Test + def entityReference() { + ''' + int x = testFunc; + ''' + .testStatements() + .expectError(IssueCodes.ReferenceToFunction, 1) + .run(); + } + + @Test + def functionCallExpression() { + ''' + InstructionSet TestISA { + functions { + signed<42> double(signed<41> value) { + return value + value; + } + + void testFunc() { + double(); + double(5); + double((long)0); + double(1, 2); + } + } + } + ''' + .testProgram() + .expectType(null, FunctionCallExpression, 8, IntegerType.signed(42)) + .expectType(null, FunctionCallExpression, 9, IntegerType.signed(42)) + .expectType(null, FunctionCallExpression, 10, IntegerType.signed(42)) + .expectType(null, FunctionCallExpression, 11, IntegerType.signed(42)) + .expectError(IssueCodes.InvalidArgumentCount, 8) + .expectError(IssueCodes.InvalidArgumentType, 10) + .expectError(IssueCodes.InvalidArgumentCount, 11) + .run(); + + ''' + int a; + a(); + a(17); + ''' + .testStatements() + .expectError(IssueCodes.InvalidFunction, 2) + .expectError(IssueCodes.InvalidFunction, 3) + .run(); + } + + @Test + def indexAccessExpression() { + ''' + InstructionSet TestISA { + architectural_state { + extern unsigned char mem[4]; + } + functions { + void testFunc() { + unsigned char arr[4]; + unsigned<4> val; + int i = 1; + long v; + + v = mem[0]; + v = mem[3:1]; + v = mem[i:i-1]; + + v = arr[0]; + v = arr[3:1]; + v = arr[i:i-1]; + + v = val[0]; + v = val[3:1]; + v = val[i:i-1]; + } + } + } + ''' + .testProgram() + .expectType(null, IndexAccessExpression, 12, IntegerType.unsigned(8)) + .expectType(null, IndexAccessExpression, 13, IntegerType.unsigned(24)) + .expectType(null, IndexAccessExpression, 14, IntegerType.unsigned(16)) + .expectType(null, IndexAccessExpression, 16, IntegerType.unsigned(8)) + .expectType(null, IndexAccessExpression, 17, IntegerType.unsigned(24)) + .expectType(null, IndexAccessExpression, 18, IntegerType.unsigned(16)) + .expectType(null, IndexAccessExpression, 20, IntegerType.unsigned(1)) + .expectType(null, IndexAccessExpression, 21, IntegerType.unsigned(3)) + .expectType(null, IndexAccessExpression, 22, IntegerType.unsigned(2)) + .run(); + + ''' + unsigned char a[4]; + long v1 = a[0]; + long v2 = a[3]; + long v3 = a[-1]; + long v4 = a[4]; + ''' + .testStatements() + .expectType(null, initializerOf('v1'), IntegerType.unsigned(8)) + .expectType(null, initializerOf('v2'), IntegerType.unsigned(8)) + .expectType(null, initializerOf('v3'), IntegerType.unsigned(8)) + .expectType(null, initializerOf('v4'), IntegerType.unsigned(8)) + .run(); + + ''' + unsigned char a[4]; + long v1 = a[0:3]; + long v2 = a[3:0]; + long v3 = a[0:1]; + long v4 = a[3:2]; + ''' + .testStatements() + .expectType(null, initializerOf('v1'), IntegerType.unsigned(32)) + .expectType(null, initializerOf('v2'), IntegerType.unsigned(32)) + .expectType(null, initializerOf('v3'), IntegerType.unsigned(16)) + .expectType(null, initializerOf('v4'), IntegerType.unsigned(16)) + .run(); + + ''' + unsigned char a[4]; + long v1 = a[a]; + long v2 = a[0:a]; + long v3 = a[a:0]; + ''' + .testStatements() + .expectError(IssueCodes.InvalidIndexType, 2) + .expectError(IssueCodes.InvalidIndexType, 3) + .expectError(IssueCodes.InvalidIndexType, 4) + .run(); + + ''' + unsigned char a[4]; + int i = 1; + + long v1 = a[i]; + long v2 = a[i:i]; + long v3 = a[i:i+1]; + long v4 = a[i:i-1]; + long v5 = a[i+1:i]; + long v6 = a[i-1:i]; + ''' + .testStatements() + .expectType(null, initializerOf('v1'), IntegerType.unsigned(8)) + .expectType(null, initializerOf('v2'), IntegerType.unsigned(8)) + .expectType(null, initializerOf('v3'), IntegerType.unsigned(16)) + .expectType(null, initializerOf('v4'), IntegerType.unsigned(16)) + .expectType(null, initializerOf('v5'), IntegerType.unsigned(16)) + .expectType(null, initializerOf('v6'), IntegerType.unsigned(16)) + .run(); + + ''' + Core TestISA { + functions { + void testFunc() { + unsigned char a[4]; + int i = 1, j = 1; + + long v1 = a[0:i]; + long v2 = a[i:0]; + long v3 = a[i:j]; + long v4 = a[i:1+i]; + long v5 = a[i:1-i]; + long v6 = a[1+i:i]; + long v7 = a[1-i:i]; + } + } + } + ''' + .testProgram() + .expectError(IssueCodes.InvalidRangePattern, 7) + .expectError(IssueCodes.InvalidRangePattern, 8) + .expectError(IssueCodes.InvalidRangePattern, 9) + .expectError(IssueCodes.InvalidRangePattern, 10) + .expectError(IssueCodes.InvalidRangePattern, 11) + .expectError(IssueCodes.InvalidRangePattern, 12) + .expectError(IssueCodes.InvalidRangePattern, 13) + .run(); + } + + @Test + def logicOperators() { + ''' + long v1 = true || false; + long v2 = 1 || 0; + long v3 = 42 || 720; + long v4 = -1 || 0xfffffffffffffffffffff; + + long v5 = true && false; + long v6 = 1 && 0; + long v7 = 42 && 720; + long v8 = -1 && 0xfffffffffffffffffffff; + ''' + .testStatements() + .expectType(null, initializerOf('v1'), IntegerType.bool) + .expectType(null, initializerOf('v2'), IntegerType.bool) + .expectType(null, initializerOf('v3'), IntegerType.bool) + .expectType(null, initializerOf('v4'), IntegerType.bool) + .expectType(null, initializerOf('v5'), IntegerType.bool) + .expectType(null, initializerOf('v6'), IntegerType.bool) + .expectType(null, initializerOf('v7'), IntegerType.bool) + .expectType(null, initializerOf('v8'), IntegerType.bool) + .run(); + + ''' + int a[4]; + long v1 = a || false; + long v2 = true || a; + long v3 = a || a; + + long v4 = a && false; + long v5 = true && a; + long v6 = a && a; + ''' + .testStatements() + .expectError(IssueCodes.InvalidOperationType, 2) + .expectError(IssueCodes.InvalidOperationType, 3) + .expectError(IssueCodes.InvalidOperationType, 4) + .expectError(IssueCodes.InvalidOperationType, 6) + .expectError(IssueCodes.InvalidOperationType, 7) + .expectError(IssueCodes.InvalidOperationType, 8) + .expectType(null, initializerOf('v1'), IntegerType.bool) + .expectType(null, initializerOf('v2'), IntegerType.bool) + .expectType(null, initializerOf('v3'), IntegerType.bool) + .expectType(null, initializerOf('v4'), IntegerType.bool) + .expectType(null, initializerOf('v5'), IntegerType.bool) + .expectType(null, initializerOf('v6'), IntegerType.bool) + .run(); + } + + @Test + def bitwiseOperators() { + ''' + long v1 = true | false; + long v2 = 6'h33 | 15'h7755; + long v3 = 8'shff | 8'haa; + + long v4 = true & false; + long v5 = 6'h33 & 15'h7755; + long v6 = 8'shff & 8'haa; + + long v7 = true ^ false; + long v8 = 6'h33 ^ 15'h7755; + long v9 = 8'shff ^ 8'haa; + ''' + .testStatements() + .expectType(null, initializerOf('v1'), IntegerType.bool) + .expectType(null, initializerOf('v2'), IntegerType.unsigned(15)) + .expectType(null, initializerOf('v3'), IntegerType.signed(8)) + .expectType(null, initializerOf('v4'), IntegerType.bool) + .expectType(null, initializerOf('v5'), IntegerType.unsigned(15)) + .expectType(null, initializerOf('v6'), IntegerType.signed(8)) + .expectType(null, initializerOf('v7'), IntegerType.bool) + .expectType(null, initializerOf('v8'), IntegerType.unsigned(15)) + .expectType(null, initializerOf('v9'), IntegerType.signed(8)) + .run(); + + ''' + int a[4]; + long v1 = a | false; + long v2 = true | a; + long v3 = a | a; + + long v4 = a & false; + long v5 = true & a; + long v6 = a & a; + + long v7 = a ^ false; + long v8 = true ^ a; + long v9 = a ^ a; + ''' + .testStatements() + .expectError(IssueCodes.InvalidOperationType, 2) + .expectError(IssueCodes.InvalidOperationType, 3) + .expectError(IssueCodes.InvalidOperationType, 4) + .expectError(IssueCodes.InvalidOperationType, 6) + .expectError(IssueCodes.InvalidOperationType, 7) + .expectError(IssueCodes.InvalidOperationType, 8) + .expectError(IssueCodes.InvalidOperationType, 10) + .expectError(IssueCodes.InvalidOperationType, 11) + .expectError(IssueCodes.InvalidOperationType, 12) + .expectType(null, initializerOf('v1'), ErrorType.invalid) + .expectType(null, initializerOf('v2'), ErrorType.invalid) + .expectType(null, initializerOf('v3'), ErrorType.invalid) + .expectType(null, initializerOf('v4'), ErrorType.invalid) + .expectType(null, initializerOf('v5'), ErrorType.invalid) + .expectType(null, initializerOf('v6'), ErrorType.invalid) + .expectType(null, initializerOf('v7'), ErrorType.invalid) + .expectType(null, initializerOf('v8'), ErrorType.invalid) + .expectType(null, initializerOf('v9'), ErrorType.invalid) + .run(); + } + + @Test + def equalityComparison() { + ''' + long v1 = true == false; + long v2 = 6'd42 == 15'd720; + long v3 = 8'shff == 8'hff; + + long v4 = true != false; + long v5 = 6'd42 != 15'd720; + long v6 = 8'shff != 8'hff; + ''' + .testStatements() + .expectType(null, initializerOf('v1'), IntegerType.bool) + .expectType(null, initializerOf('v2'), IntegerType.bool) + .expectType(null, initializerOf('v3'), IntegerType.bool) + .expectType(null, initializerOf('v4'), IntegerType.bool) + .expectType(null, initializerOf('v5'), IntegerType.bool) + .expectType(null, initializerOf('v6'), IntegerType.bool) + .run(); + + ''' + int a[4]; + long v1 = a == false; + long v2 = true == a; + long v3 = a == a; + + long v4 = a != false; + long v5 = true != a; + long v6 = a != a; + ''' + .testStatements() + .expectError(IssueCodes.InvalidOperationType, 2) + .expectError(IssueCodes.InvalidOperationType, 3) + .expectError(IssueCodes.InvalidOperationType, 6) + .expectError(IssueCodes.InvalidOperationType, 7) + .expectType(null, initializerOf('v1'), IntegerType.bool) + .expectType(null, initializerOf('v2'), IntegerType.bool) + .expectType(null, initializerOf('v3'), IntegerType.bool) + .expectType(null, initializerOf('v4'), IntegerType.bool) + .expectType(null, initializerOf('v5'), IntegerType.bool) + .expectType(null, initializerOf('v6'), IntegerType.bool) + .run(); + + ''' + InstructionSet TestISA { + architectural_state { + extern unsigned char mem[4]; + } + functions { + void testFunc() { + bool equal = mem == mem; + } + } + } + ''' + .testProgram() + .expectError(IssueCodes.InvalidOperationType, 7) + .run(); + } + + @Test + def magnitudeComparison() { + ''' + long v1 = 6'd42 < 15'd720; + long v2 = 8'shff < 8'hff; + + long v3 = 6'd42 > 15'd720; + long v4 = 8'shff > 8'hff; + + long v5 = 6'd42 <= 15'd720; + long v6 = 8'shff <= 8'hff; + + long v7 = 6'd42 >= 15'd720; + long v8 = 8'shff >= 8'hff; + ''' + .testStatements() + .expectType(null, initializerOf('v1'), IntegerType.bool) + .expectType(null, initializerOf('v2'), IntegerType.bool) + .expectType(null, initializerOf('v3'), IntegerType.bool) + .expectType(null, initializerOf('v4'), IntegerType.bool) + .expectType(null, initializerOf('v5'), IntegerType.bool) + .expectType(null, initializerOf('v6'), IntegerType.bool) + .expectType(null, initializerOf('v7'), IntegerType.bool) + .expectType(null, initializerOf('v8'), IntegerType.bool) + .run(); + + ''' + int a[4]; + long v1 = a < 0; + long v2 = 0 < a; + long v3 = a < a; + + long v4 = a > 0; + long v5 = 0 > a; + long v6 = a > a; + + long v7 = a <= 0; + long v8 = 0 <= a; + long v9 = a <= a; + + long v10 = a >= 0; + long v11 = 0 >= a; + long v12 = a >= a; + ''' + .testStatements() + .expectError(IssueCodes.InvalidOperationType, 2) + .expectError(IssueCodes.InvalidOperationType, 3) + .expectError(IssueCodes.InvalidOperationType, 4) + .expectError(IssueCodes.InvalidOperationType, 6) + .expectError(IssueCodes.InvalidOperationType, 7) + .expectError(IssueCodes.InvalidOperationType, 8) + .expectError(IssueCodes.InvalidOperationType, 10) + .expectError(IssueCodes.InvalidOperationType, 11) + .expectError(IssueCodes.InvalidOperationType, 12) + .expectError(IssueCodes.InvalidOperationType, 14) + .expectError(IssueCodes.InvalidOperationType, 15) + .expectError(IssueCodes.InvalidOperationType, 16) + .expectType(null, initializerOf('v1'), IntegerType.bool) + .expectType(null, initializerOf('v2'), IntegerType.bool) + .expectType(null, initializerOf('v3'), IntegerType.bool) + .expectType(null, initializerOf('v4'), IntegerType.bool) + .expectType(null, initializerOf('v5'), IntegerType.bool) + .expectType(null, initializerOf('v6'), IntegerType.bool) + .expectType(null, initializerOf('v7'), IntegerType.bool) + .expectType(null, initializerOf('v8'), IntegerType.bool) + .expectType(null, initializerOf('v9'), IntegerType.bool) + .expectType(null, initializerOf('v10'), IntegerType.bool) + .expectType(null, initializerOf('v11'), IntegerType.bool) + .expectType(null, initializerOf('v12'), IntegerType.bool) + .run(); + } + + @Test + def shiftOperators() { + ''' + long v1 = 42 << 2; + long v2 = (-77) << 3; + long v3 = 1 << 6; + + long v4 = 42 >> 2; + long v5 = (-77) >> 3; + long v6 = 1 << 6; + ''' + .testStatements() + .expectWarning(IssueCodes.ShiftAlwaysZero, 3) + .expectWarning(IssueCodes.ShiftAlwaysZero, 7) + .expectType(null, initializerOf('v1'), IntegerType.unsigned(6)) + .expectType(null, initializerOf('v2'), IntegerType.signed(8)) + .expectType(null, initializerOf('v3'), IntegerType.bool) + .expectType(null, initializerOf('v4'), IntegerType.unsigned(6)) + .expectType(null, initializerOf('v5'), IntegerType.signed(8)) + .expectType(null, initializerOf('v6'), IntegerType.bool) + .run(); + + ''' + int a[4]; + long v1 = a << 0; + long v2 = 0 << a; + long v3 = a << a; + + long v4 = a >> 0; + long v5 = 0 >> a; + long v6 = a >> a; + ''' + .testStatements() + .expectError(IssueCodes.InvalidOperationType, 2) + .expectError(IssueCodes.InvalidOperationType, 3) + .expectError(IssueCodes.InvalidOperationType, 4) + .expectError(IssueCodes.InvalidOperationType, 6) + .expectError(IssueCodes.InvalidOperationType, 7) + .expectError(IssueCodes.InvalidOperationType, 8) + .expectType(null, initializerOf('v1'), ErrorType.invalid) + .expectType(null, initializerOf('v2'), ErrorType.invalid) + .expectType(null, initializerOf('v3'), ErrorType.invalid) + .expectType(null, initializerOf('v4'), ErrorType.invalid) + .expectType(null, initializerOf('v5'), ErrorType.invalid) + .expectType(null, initializerOf('v6'), ErrorType.invalid) + .run(); + } + + @Test + def addition() { + ''' + long v1 = 6'd0 + 4'd0; + long v2 = 6'sd0 + 4'sd0; + long v3 = 6'sd0 + 4'd0; + long v4 = 6'd0 + 4'sd0; + ''' + .testStatements() + .expectType(null, initializerOf('v1'), IntegerType.unsigned(7)) + .expectType(null, initializerOf('v2'), IntegerType.signed(7)) + .expectType(null, initializerOf('v3'), IntegerType.signed(7)) + .expectType(null, initializerOf('v4'), IntegerType.signed(8)) + .run(); + + ''' + int a[4]; + long v1 = a + 0; + long v2 = 0 + a; + long v3 = a + a; + ''' + .testStatements() + .expectError(IssueCodes.InvalidOperationType, 2) + .expectError(IssueCodes.InvalidOperationType, 3) + .expectError(IssueCodes.InvalidOperationType, 4) + .expectType(null, initializerOf('v1'), ErrorType.invalid) + .expectType(null, initializerOf('v2'), ErrorType.invalid) + .expectType(null, initializerOf('v3'), ErrorType.invalid) + .run(); + } + + @Test + def subtraction() { + ''' + long v1 = 6'd0 - 4'd0; + long v2 = 6'sd0 - 4'sd0; + long v3 = 6'sd0 - 4'd0; + long v4 = 6'd0 - 4'sd0; + ''' + .testStatements() + .expectType(null, initializerOf('v1'), IntegerType.signed(7)) + .expectType(null, initializerOf('v2'), IntegerType.signed(7)) + .expectType(null, initializerOf('v3'), IntegerType.signed(7)) + .expectType(null, initializerOf('v4'), IntegerType.signed(8)) + .run(); + + ''' + int a[4]; + long v1 = a - 0; + long v2 = 0 - a; + long v3 = a - a; + ''' + .testStatements() + .expectError(IssueCodes.InvalidOperationType, 2) + .expectError(IssueCodes.InvalidOperationType, 3) + .expectError(IssueCodes.InvalidOperationType, 4) + .expectType(null, initializerOf('v1'), ErrorType.invalid) + .expectType(null, initializerOf('v2'), ErrorType.invalid) + .expectType(null, initializerOf('v3'), ErrorType.invalid) + .run(); + } + + @Test + def multiplication() { + ''' + long v1 = 6'd0 * 4'd0; + long v2 = 6'sd0 * 4'sd0; + long v3 = 6'sd0 * 4'd0; + long v4 = 6'd0 * 4'sd0; + ''' + .testStatements() + .expectType(null, initializerOf('v1'), IntegerType.unsigned(10)) + .expectType(null, initializerOf('v2'), IntegerType.signed(10)) + .expectType(null, initializerOf('v3'), IntegerType.signed(10)) + .expectType(null, initializerOf('v4'), IntegerType.signed(10)) + .run(); + + ''' + int a[4]; + long v1 = a * 0; + long v2 = 0 * a; + long v3 = a * a; + ''' + .testStatements() + .expectError(IssueCodes.InvalidOperationType, 2) + .expectError(IssueCodes.InvalidOperationType, 3) + .expectError(IssueCodes.InvalidOperationType, 4) + .expectType(null, initializerOf('v1'), ErrorType.invalid) + .expectType(null, initializerOf('v2'), ErrorType.invalid) + .expectType(null, initializerOf('v3'), ErrorType.invalid) + .run(); + } + + @Test + def division() { + ''' + long v1 = 6'd0 / 4'd0; + long v2 = 6'sd0 / 4'sd0; + long v3 = 6'sd0 / 4'd0; + long v4 = 6'd0 / 4'sd0; + ''' + .testStatements() + .evaluateExpressions() + .expectError(IssueCodes.DivisionByZero, 1) + .expectError(IssueCodes.DivisionByZero, 2) + .expectError(IssueCodes.DivisionByZero, 3) + .expectError(IssueCodes.DivisionByZero, 4) + .expectType(null, initializerOf('v1'), IntegerType.unsigned(6)) + .expectType(null, initializerOf('v2'), IntegerType.signed(7)) + .expectType(null, initializerOf('v3'), IntegerType.signed(6)) + .expectType(null, initializerOf('v4'), IntegerType.signed(7)) + .run(); + + ''' + int a[4]; + long v1 = a / 1; + long v2 = 1 / a; + long v3 = a / a; + ''' + .testStatements() + .expectError(IssueCodes.InvalidOperationType, 2) + .expectError(IssueCodes.InvalidOperationType, 3) + .expectError(IssueCodes.InvalidOperationType, 4) + .expectType(null, initializerOf('v1'), ErrorType.invalid) + .expectType(null, initializerOf('v2'), ErrorType.invalid) + .expectType(null, initializerOf('v3'), ErrorType.invalid) + .run(); + } + + @Test + def modulo() { + ''' + long v1 = 6'd0 % 4'd0; + long v2 = 6'sd0 % 4'sd0; + long v3 = 6'sd0 % 4'd0; + long v4 = 6'd0 % 4'sd0; + long v5 = 4'sd0 % 6'd0; + long v6 = 4'd0 % 6'sd0; + ''' + .testStatements() + // TODO implement constant expression evaluation for modulo + //.evaluateExpressions() + //.expectError(IssueCodes.DivisionByZero, 1) + //.expectError(IssueCodes.DivisionByZero, 2) + //.expectError(IssueCodes.DivisionByZero, 3) + //.expectError(IssueCodes.DivisionByZero, 4) + //.expectError(IssueCodes.DivisionByZero, 5) + //.expectError(IssueCodes.DivisionByZero, 6) + .expectType(null, initializerOf('v1'), IntegerType.unsigned(4)) + .expectType(null, initializerOf('v2'), IntegerType.signed(4)) + .expectType(null, initializerOf('v3'), IntegerType.signed(5)) + .expectType(null, initializerOf('v4'), IntegerType.unsigned(3)) + .expectType(null, initializerOf('v5'), IntegerType.signed(4)) + .expectType(null, initializerOf('v6'), IntegerType.unsigned(4)) + .run(); + + ''' + int a[4]; + long v1 = a % 1; + long v2 = 1 % a; + long v3 = a % a; + ''' + .testStatements() + .expectError(IssueCodes.InvalidOperationType, 2) + .expectError(IssueCodes.InvalidOperationType, 3) + .expectError(IssueCodes.InvalidOperationType, 4) + .expectType(null, initializerOf('v1'), ErrorType.invalid) + .expectType(null, initializerOf('v2'), ErrorType.invalid) + .expectType(null, initializerOf('v3'), ErrorType.invalid) + .run(); + } + + @Test + def incrementDecrement() { + ''' + int a = 0; + long v1 = a++; + long v2 = a--; + long v3 = ++a; + long v4 = --a; + ''' + .testStatements() + .expectType(null, initializerOf('v1'), IntegerType.signed(32)) + .expectType(null, initializerOf('v2'), IntegerType.signed(32)) + .expectType(null, initializerOf('v3'), IntegerType.signed(32)) + .expectType(null, initializerOf('v4'), IntegerType.signed(32)) + .run(); + + ''' + int a[4]; + long v1 = a++; + long v2 = a--; + long v3 = ++a; + long v4 = --a; + ''' + .testStatements() + .expectError(IssueCodes.InvalidOperationType, 2) + .expectError(IssueCodes.InvalidOperationType, 3) + .expectError(IssueCodes.InvalidOperationType, 4) + .expectError(IssueCodes.InvalidOperationType, 5) + .run(); + } + + @Test + def prefix() { + ''' + int a = 0; + long v1 = ~a; + long v2 = -a; + long v3 = +a; + long v4 = !a; + ''' + .testStatements() + .expectType(null, initializerOf('v1'), IntegerType.signed(32)) + .expectType(null, initializerOf('v2'), IntegerType.signed(33)) + .expectType(null, initializerOf('v3'), IntegerType.signed(32)) + .expectType(null, initializerOf('v4'), IntegerType.bool) + .run(); + + ''' + int a[4]; + long v1 = ~a; + long v2 = -a; + long v3 = +a; + long v4 = !a; + ''' + .testStatements() + .expectError(IssueCodes.InvalidOperationType, 2) + .expectError(IssueCodes.InvalidOperationType, 3) + .expectError(IssueCodes.InvalidOperationType, 4) + .expectError(IssueCodes.InvalidOperationType, 5) + .run(); + } } diff --git a/com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslStatementTest.xtend b/com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslStatementTest.xtend index 6e632bda..d5fcf9b9 100644 --- a/com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslStatementTest.xtend +++ b/com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslStatementTest.xtend @@ -151,6 +151,16 @@ class CoreDslStatementTest { .expectError(IssueCodes.NonScalarCondition, 13) .run(); + ''' + bool b; + + if(b = 0); + if(b == 0); + ''' + .testStatements() + .expectWarning(IssueCodes.LikelyAccidentalAssignment, 3) + .run(); + ''' if(true); if(false); @@ -272,6 +282,16 @@ class CoreDslStatementTest { .expectError(IssueCodes.NonScalarCondition, 13) .run(); + ''' + bool b; + + while(b = 0); + while(b == 0); + ''' + .testStatements() + .expectWarning(IssueCodes.LikelyAccidentalAssignment, 3) + .run(); + ''' while(true); while(false); @@ -307,6 +327,16 @@ class CoreDslStatementTest { .expectError(IssueCodes.NonScalarCondition, 13) .run(); + ''' + bool b; + + do ; while(b = 0); + do ; while(b == 0); + ''' + .testStatements() + .expectWarning(IssueCodes.LikelyAccidentalAssignment, 3) + .run(); + ''' do ; while(true); do ; while(false); @@ -342,6 +372,16 @@ class CoreDslStatementTest { .expectError(IssueCodes.NonScalarCondition, 13) .run(); + ''' + bool b; + + for(;b = 0;); + for(;b == 0;); + ''' + .testStatements() + .expectWarning(IssueCodes.LikelyAccidentalAssignment, 3) + .run(); + ''' for(;true;); for(;false;); diff --git a/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslAnalyzer.xtend b/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslAnalyzer.xtend index 4610bb24..cf254ac9 100644 --- a/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslAnalyzer.xtend +++ b/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslAnalyzer.xtend @@ -1238,7 +1238,7 @@ class CoreDslAnalyzer { /** * 1. The condition must be an expression with a scalar type. (NonScalarCondition)
- * 2. One of the two options' type has to be implicitly convertible to the other one. (IncompatibleOptionTypes) + * 2. One of the two options' type has to be implicitly convertible to the other one, or both needs to be integer types. (IncompatibleOptionTypes) */ def static dispatch CoreDslType analyzeExpression(AnalysisContext ctx, ConditionalExpression expression) { val conditionType = analyzeExpression(ctx, expression.condition); @@ -1361,9 +1361,11 @@ class CoreDslAnalyzer { } val elementCount = getRangeSize(ctx, expression.index, expression.endIndex); - if(elementCount === null && !ctx.isPartialAnalysis) { - ctx.acceptError("Invalid range pattern", expression, - CoreDslPackage.Literals.INDEX_ACCESS_EXPRESSION__TCOLON, -1, IssueCodes.InvalidRangePattern); + if(elementCount === null) { + if(!ctx.isPartialAnalysis) { + ctx.acceptError("Invalid range pattern", expression, + CoreDslPackage.Literals.INDEX_ACCESS_EXPRESSION__TCOLON, -1, IssueCodes.InvalidRangePattern); + } return ctx.setExpressionType(expression, ErrorType.invalid); } @@ -1387,7 +1389,12 @@ class CoreDslAnalyzer { * Implements the patterns described here. */ def static BigInteger getRangeSize(AnalysisContext ctx, Expression start, Expression end) { - if(start instanceof EntityReference && end instanceof InfixExpression || + if(start instanceof EntityReference && end instanceof EntityReference) { + if((start as EntityReference).target == (end as EntityReference).target) { + return BigInteger.ONE; + } + } + else if(start instanceof EntityReference && end instanceof InfixExpression || start instanceof InfixExpression && end instanceof EntityReference) { val reference = start instanceof EntityReference ? start : end as EntityReference; val infix = start instanceof InfixExpression ? start : end as InfixExpression; @@ -1489,7 +1496,13 @@ class CoreDslAnalyzer { } case '==', case '!=': { - if(leftType != rightType && !leftType.isIntegerType || !rightType.isIntegerType) { + if(leftType.isAddressSpaceType || rightType.isAddressSpaceType) { + ctx.acceptError( + "Equality comparisons on address spaces are not allowed", + expression, CoreDslPackage.Literals.INFIX_EXPRESSION__OPERATOR, -1, + IssueCodes.InvalidOperationType); + } + else if(leftType != rightType && (!leftType.isIntegerType || !rightType.isIntegerType)) { ctx.acceptError( "Equality comparisons are only valid on integer types or two operands of the same type", expression, CoreDslPackage.Literals.INFIX_EXPRESSION__OPERATOR, -1, @@ -1703,6 +1716,7 @@ class CoreDslAnalyzer { if(!operandType.isIntegerType && !operandType.isError) { ctx.acceptError("Increment and decrement operators are only valid on integer types", expression, CoreDslPackage.Literals.POSTFIX_EXPRESSION__OPERATOR, -1, IssueCodes.InvalidOperationType); + return ctx.setExpressionType(expression, ErrorType.invalid); } return ctx.setExpressionType(expression, operandType); @@ -1741,6 +1755,7 @@ class CoreDslAnalyzer { if(!operandType.isIntegerType && !operandType.isError) { ctx.acceptError("Increment and decrement operators are only valid on integer types", expression, CoreDslPackage.Literals.PREFIX_EXPRESSION__OPERATOR, -1, IssueCodes.InvalidOperationType); + return ctx.setExpressionType(expression, ErrorType.invalid); } return ctx.setExpressionType(expression, operandType); @@ -1749,6 +1764,7 @@ class CoreDslAnalyzer { if(!operandType.isIntegerType && !operandType.isError) { ctx.acceptError("Bitwise negation is only valid on integer types", expression, CoreDslPackage.Literals.PREFIX_EXPRESSION__OPERATOR, -1, IssueCodes.InvalidOperationType); + return ctx.setExpressionType(expression, ErrorType.invalid); } return ctx.setExpressionType(expression, operandType); @@ -1757,6 +1773,7 @@ class CoreDslAnalyzer { if(!operandType.isIntegerType && !operandType.isError) { ctx.acceptError("Unary plus is only valid on integer types", expression, CoreDslPackage.Literals.PREFIX_EXPRESSION__OPERATOR, -1, IssueCodes.InvalidOperationType); + return ctx.setExpressionType(expression, ErrorType.invalid); } return ctx.setExpressionType(expression, operandType); @@ -1765,10 +1782,10 @@ class CoreDslAnalyzer { if(!operandType.isIntegerType && !operandType.isError) { ctx.acceptError("Unary minus is only valid on integer types", expression, CoreDslPackage.Literals.PREFIX_EXPRESSION__OPERATOR, -1, IssueCodes.InvalidOperationType); + return ctx.setExpressionType(expression, ErrorType.invalid); } - val intType = operandType as IntegerType; - return ctx.setExpressionType(expression, IntegerType.signed(intType.bitSize + 1)); + return ctx.setExpressionType(expression, IntegerType.signed(operandType.bitSize + 1)); } case '!': { if(!operandType.isScalarType && !operandType.isError) { diff --git a/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslConstantExpressionEvaluator.xtend b/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslConstantExpressionEvaluator.xtend index 782ecc1c..6d6b4735 100644 --- a/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslConstantExpressionEvaluator.xtend +++ b/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslConstantExpressionEvaluator.xtend @@ -108,6 +108,8 @@ class CoreDslConstantExpressionEvaluator { ctx.acceptError("Division by zero", expression, CoreDslPackage.Literals.INFIX_EXPRESSION__OPERATOR, -1, IssueCodes.DivisionByZero); } + // TODO these don't work correctly at the moment, because the operands first need to be zero-extended + // to the result type first, but neither the operand types, nor the result type are known case '&': return new ConstantValue(left.value.and(right.value)) case '|': diff --git a/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslTypeProvider.xtend b/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslTypeProvider.xtend index 90ef4b08..8ed9de64 100644 --- a/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslTypeProvider.xtend +++ b/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslTypeProvider.xtend @@ -168,6 +168,7 @@ abstract class CoreDslTypeProvider { def static boolean canImplicitlyConvert(CoreDslType from, CoreDslType to) { // allow all conversions involving error types to avoid follow-up errors if(from.isError || to.isError) return true; + if(from == to) return true; if(from.isIntegerType && to.isIntegerType) { return canImplicitlyConvert(from as IntegerType, to as IntegerType); diff --git a/com.minres.coredsl/src/com/minres/coredsl/type/ArrayType.xtend b/com.minres.coredsl/src/com/minres/coredsl/type/ArrayType.xtend index 90e1164e..e1cda7a3 100644 --- a/com.minres.coredsl/src/com/minres/coredsl/type/ArrayType.xtend +++ b/com.minres.coredsl/src/com/minres/coredsl/type/ArrayType.xtend @@ -25,6 +25,13 @@ class ArrayType extends AggregateType { override toString() { return isUnknownSize ? '''«elementType»[?]''' : '''«elementType»[«count»]'''; } + override equals(Object obj) { + if(obj instanceof ArrayType) { + return obj.count == count && obj.isUnknownSize == isUnknownSize && obj.elementType == elementType; + } + return false; + } + def static ofUnknownSize(CoreDslType elementType) { return new ArrayType(elementType); } diff --git a/com.minres.coredsl/src/com/minres/coredsl/type/CompositeType.xtend b/com.minres.coredsl/src/com/minres/coredsl/type/CompositeType.xtend index 05a6133c..768e3a72 100644 --- a/com.minres.coredsl/src/com/minres/coredsl/type/CompositeType.xtend +++ b/com.minres.coredsl/src/com/minres/coredsl/type/CompositeType.xtend @@ -21,5 +21,12 @@ class CompositeType extends CoreDslType { override isStructType() { return declaration instanceof StructTypeDeclaration; } override isUnionType() { return declaration instanceof UnionTypeDeclaration; } + override equals(Object obj) { + if(obj instanceof CompositeType) { + return obj.declaration === declaration; + } + return false; + } + override toString() { return isStructType ? '''struct «declaration.name»''' : '''union «declaration.name»'''; } } diff --git a/com.minres.coredsl/src/com/minres/coredsl/type/IntegerType.xtend b/com.minres.coredsl/src/com/minres/coredsl/type/IntegerType.xtend index aa9e3956..977cf00e 100644 --- a/com.minres.coredsl/src/com/minres/coredsl/type/IntegerType.xtend +++ b/com.minres.coredsl/src/com/minres/coredsl/type/IntegerType.xtend @@ -25,4 +25,11 @@ class IntegerType extends CoreDslType { override isIntegerType() { return true; } override toString() { return isSigned ? '''signed<«bitSize»>''' : '''unsigned<«bitSize»>'''; } + + override equals(Object obj) { + if(obj instanceof IntegerType) { + return obj.bitSize == bitSize && obj.isSigned == isSigned; + } + return false; + } }