mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-11-23 08:32:39 +00:00
Implemented call parameter type checking in pass 1.
This commit is contained in:
parent
d58c46e46f
commit
a180a4c0b4
@ -164,7 +164,7 @@ public class Compiler {
|
|||||||
new Pass1Procedures(program).execute();
|
new Pass1Procedures(program).execute();
|
||||||
new PassNTypeInference(program).execute();
|
new PassNTypeInference(program).execute();
|
||||||
new PassNTypeIdSimplification(program).execute();
|
new PassNTypeIdSimplification(program).execute();
|
||||||
|
new Pass1AssertProcedureCallParameters(program).execute();
|
||||||
new Pass1UnwindStructValues(program).execute();
|
new Pass1UnwindStructValues(program).execute();
|
||||||
|
|
||||||
if(getLog().isVerbosePass1CreateSsa()) {
|
if(getLog().isVerbosePass1CreateSsa()) {
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
package dk.camelot64.kickc.passes;
|
||||||
|
|
||||||
|
import dk.camelot64.kickc.model.CompileError;
|
||||||
|
import dk.camelot64.kickc.model.ControlFlowBlock;
|
||||||
|
import dk.camelot64.kickc.model.Program;
|
||||||
|
import dk.camelot64.kickc.model.statements.Statement;
|
||||||
|
import dk.camelot64.kickc.model.statements.StatementAssignment;
|
||||||
|
import dk.camelot64.kickc.model.statements.StatementCall;
|
||||||
|
import dk.camelot64.kickc.model.statements.StatementConditionalJump;
|
||||||
|
import dk.camelot64.kickc.model.symbols.Procedure;
|
||||||
|
import dk.camelot64.kickc.model.symbols.Variable;
|
||||||
|
import dk.camelot64.kickc.model.types.SymbolType;
|
||||||
|
import dk.camelot64.kickc.model.types.SymbolTypeConversion;
|
||||||
|
import dk.camelot64.kickc.model.types.SymbolTypeInference;
|
||||||
|
import dk.camelot64.kickc.model.values.*;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that all calls have parameters of the types that the procedure expects
|
||||||
|
*/
|
||||||
|
public class Pass1AssertProcedureCallParameters extends Pass1Base {
|
||||||
|
|
||||||
|
public Pass1AssertProcedureCallParameters(Program program) {
|
||||||
|
super(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean step() {
|
||||||
|
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||||
|
for(Statement statement : block.getStatements()) {
|
||||||
|
if(statement instanceof StatementCall) {
|
||||||
|
StatementCall call = (StatementCall) statement;
|
||||||
|
Procedure procedure = getScope().getProcedure(call.getProcedure());
|
||||||
|
List<Variable> declParameters = procedure.getParameters();
|
||||||
|
List<RValue> callParameters = call.getParameters();
|
||||||
|
if(callParameters.size()!=declParameters.size()) {
|
||||||
|
throw new CompileError("Wrong number of parameters in call "+call.toString(getProgram(), false)+" expected "+procedure.toString(getProgram()), statement);
|
||||||
|
}
|
||||||
|
for(int i = 0; i < declParameters.size(); i++) {
|
||||||
|
Variable declParameter = declParameters.get(i);
|
||||||
|
RValue callParameter = callParameters.get(i);
|
||||||
|
SymbolType callParameterType = SymbolTypeInference.inferType(getScope(), callParameter);
|
||||||
|
SymbolType declParameterType = declParameter.getType();
|
||||||
|
if(!SymbolTypeConversion.assignmentTypeMatch(declParameterType, callParameterType)) {
|
||||||
|
throw new CompileError("Parameters type mismatch in call "+call.toString(getProgram(), false)+" expected "+procedure.toString(getProgram()), statement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -51,8 +51,9 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(Procedure procedure : getScope().getAllProcedures(true)) {
|
|
||||||
|
|
||||||
|
// Iterate through all procedures changing parameter lists by unwinding each struct value parameter
|
||||||
|
for(Procedure procedure : getScope().getAllProcedures(true)) {
|
||||||
ArrayList<String> unwoundParameterNames = new ArrayList<>();
|
ArrayList<String> unwoundParameterNames = new ArrayList<>();
|
||||||
boolean procedureUnwound = false;
|
boolean procedureUnwound = false;
|
||||||
for(Variable parameter : procedure.getParameters()) {
|
for(Variable parameter : procedure.getParameters()) {
|
||||||
@ -72,8 +73,7 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unwind all references to struct values into members
|
||||||
// Unwind all references to full structs
|
|
||||||
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||||
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
|
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
|
||||||
while(stmtIt.hasNext()) {
|
while(stmtIt.hasNext()) {
|
||||||
@ -83,44 +83,7 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
if(assignment.getlValue() instanceof VariableRef) {
|
if(assignment.getlValue() instanceof VariableRef) {
|
||||||
Variable assignedVar = getScope().getVariable((VariableRef) assignment.getlValue());
|
Variable assignedVar = getScope().getVariable((VariableRef) assignment.getlValue());
|
||||||
if(assignedVar.getType() instanceof SymbolTypeStruct) {
|
if(assignedVar.getType() instanceof SymbolTypeStruct) {
|
||||||
// Assigning a struct!
|
unwindStructAssignment(assignment, assignedVar, stmtIt, structUnwinding);
|
||||||
if(assignment.getOperator() == null && assignment.getrValue2() instanceof StructZero) {
|
|
||||||
// Initializing a struct - unwind to assigning zero to each member!
|
|
||||||
StructUnwinding.VariableUnwinding variableUnwinding = structUnwinding.getVariableUnwinding(assignedVar.getRef());
|
|
||||||
if(variableUnwinding != null) {
|
|
||||||
stmtIt.previous();
|
|
||||||
for(String memberName : variableUnwinding.getMemberNames()) {
|
|
||||||
VariableRef memberVarRef = variableUnwinding.getMemberUnwinding(memberName);
|
|
||||||
Variable memberVar = getScope().getVariable(memberVarRef);
|
|
||||||
Statement initStmt = Pass0GenerateStatementSequence.createDefaultInitializationStatement(memberVarRef, memberVar.getType(), statement.getSource(), Comment.NO_COMMENTS);
|
|
||||||
stmtIt.add(initStmt);
|
|
||||||
getLog().append("Adding struct value member variable default initializer " + initStmt.toString(getProgram(), false));
|
|
||||||
}
|
|
||||||
stmtIt.next();
|
|
||||||
stmtIt.remove();
|
|
||||||
}
|
|
||||||
} else if(assignment.getOperator() == null && assignment.getrValue2() instanceof VariableRef) {
|
|
||||||
Variable sourceVar = getScope().getVariable((VariableRef) assignment.getrValue2());
|
|
||||||
if(sourceVar.getType().equals(assignedVar.getType())) {
|
|
||||||
// Copying a struct - unwind to assigning each member!
|
|
||||||
StructUnwinding.VariableUnwinding assignedMemberVariables = structUnwinding.getVariableUnwinding(assignedVar.getRef());
|
|
||||||
StructUnwinding.VariableUnwinding sourceMemberVariables = structUnwinding.getVariableUnwinding(sourceVar.getRef());
|
|
||||||
if(assignedMemberVariables != null && sourceMemberVariables != null) {
|
|
||||||
stmtIt.previous();
|
|
||||||
for(String memberName : assignedMemberVariables.getMemberNames()) {
|
|
||||||
VariableRef assignedMemberVarRef = assignedMemberVariables.getMemberUnwinding(memberName);
|
|
||||||
VariableRef sourceMemberVarRef = sourceMemberVariables.getMemberUnwinding(memberName);
|
|
||||||
Statement copyStmt = new StatementAssignment(assignedMemberVarRef, sourceMemberVarRef, statement.getSource(), Comment.NO_COMMENTS);
|
|
||||||
stmtIt.add(copyStmt);
|
|
||||||
getLog().append("Adding struct value member variable copy " + copyStmt.toString(getProgram(), false));
|
|
||||||
}
|
|
||||||
stmtIt.next();
|
|
||||||
stmtIt.remove();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new CompileError("Incompatible struct assignment " + statement.toString(getProgram(), false), statement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(statement instanceof StatementCall) {
|
} else if(statement instanceof StatementCall) {
|
||||||
@ -179,6 +142,56 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
return modified;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unwind an assignment to a struct value variable into assignment of each member
|
||||||
|
* @param assignment The assignment statement
|
||||||
|
* @param assignedVar The struct value variable being assigned to (the LValue)
|
||||||
|
* @param stmtIt The statement iterator used for adding/removing statements
|
||||||
|
* @param structUnwinding Information about unwound struct value variables
|
||||||
|
*/
|
||||||
|
public void unwindStructAssignment(StatementAssignment assignment, Variable assignedVar, ListIterator<Statement> stmtIt, StructUnwinding structUnwinding) {
|
||||||
|
// Assigning a struct!
|
||||||
|
if(assignment.getOperator() == null && assignment.getrValue2() instanceof StructZero) {
|
||||||
|
// Initializing a struct - unwind to assigning zero to each member!
|
||||||
|
StructUnwinding.VariableUnwinding variableUnwinding = structUnwinding.getVariableUnwinding(assignedVar.getRef());
|
||||||
|
if(variableUnwinding != null) {
|
||||||
|
stmtIt.previous();
|
||||||
|
for(String memberName : variableUnwinding.getMemberNames()) {
|
||||||
|
VariableRef memberVarRef = variableUnwinding.getMemberUnwinding(memberName);
|
||||||
|
Variable memberVar = getScope().getVariable(memberVarRef);
|
||||||
|
Statement initStmt = Pass0GenerateStatementSequence.createDefaultInitializationStatement(memberVarRef, memberVar.getType(), assignment.getSource(), Comment.NO_COMMENTS);
|
||||||
|
stmtIt.add(initStmt);
|
||||||
|
getLog().append("Adding struct value member variable default initializer " + initStmt.toString(getProgram(), false));
|
||||||
|
}
|
||||||
|
stmtIt.next();
|
||||||
|
stmtIt.remove();
|
||||||
|
}
|
||||||
|
} else if(assignment.getOperator() == null && assignment.getrValue2() instanceof VariableRef) {
|
||||||
|
Variable sourceVar = getScope().getVariable((VariableRef) assignment.getrValue2());
|
||||||
|
if(sourceVar.getType().equals(assignedVar.getType())) {
|
||||||
|
// Copying a struct - unwind to assigning each member!
|
||||||
|
StructUnwinding.VariableUnwinding assignedMemberVariables = structUnwinding.getVariableUnwinding(assignedVar.getRef());
|
||||||
|
StructUnwinding.VariableUnwinding sourceMemberVariables = structUnwinding.getVariableUnwinding(sourceVar.getRef());
|
||||||
|
if(assignedMemberVariables != null && sourceMemberVariables != null) {
|
||||||
|
stmtIt.previous();
|
||||||
|
for(String memberName : assignedMemberVariables.getMemberNames()) {
|
||||||
|
VariableRef assignedMemberVarRef = assignedMemberVariables.getMemberUnwinding(memberName);
|
||||||
|
VariableRef sourceMemberVarRef = sourceMemberVariables.getMemberUnwinding(memberName);
|
||||||
|
Statement copyStmt = new StatementAssignment(assignedMemberVarRef, sourceMemberVarRef, assignment.getSource(), Comment.NO_COMMENTS);
|
||||||
|
stmtIt.add(copyStmt);
|
||||||
|
getLog().append("Adding struct value member variable copy " + copyStmt.toString(getProgram(), false));
|
||||||
|
}
|
||||||
|
stmtIt.next();
|
||||||
|
stmtIt.remove();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new CompileError("Incompatible struct assignment " + assignment.toString(getProgram(), false), assignment);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new CompileError("Incompatible struct assignment " + assignment.toString(getProgram(), false), assignment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keeps track of all structs that have been unwound into member variables.
|
* Keeps track of all structs that have been unwound into member variables.
|
||||||
*/
|
*/
|
||||||
|
@ -51,43 +51,53 @@ public class TestPrograms {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testError1() throws IOException, URISyntaxException {
|
public void testStructError3() throws IOException, URISyntaxException {
|
||||||
|
assertError("struct-err-3", "Parameters type mismatch in call");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStructError2() throws IOException, URISyntaxException {
|
||||||
|
assertError("struct-err-2", "Incompatible struct assignment");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStructError1() throws IOException, URISyntaxException {
|
||||||
assertError("struct-err-1", "Incompatible struct assignment");
|
assertError("struct-err-1", "Incompatible struct assignment");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testError0() throws IOException, URISyntaxException {
|
public void testStructError0() throws IOException, URISyntaxException {
|
||||||
assertError("struct-err-0", "Unknown struct type");
|
assertError("struct-err-0", "Unknown struct type");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStruct5() throws IOException, URISyntaxException {
|
public void testStruct5() throws IOException, URISyntaxException {
|
||||||
compileAndCompare("struct-5", log());
|
compileAndCompare("struct-5");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStruct4() throws IOException, URISyntaxException {
|
public void testStruct4() throws IOException, URISyntaxException {
|
||||||
compileAndCompare("struct-4", log());
|
compileAndCompare("struct-4");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStruct3() throws IOException, URISyntaxException {
|
public void testStruct3() throws IOException, URISyntaxException {
|
||||||
compileAndCompare("struct-3", log().verboseCreateSsa());
|
compileAndCompare("struct-3");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStruct2() throws IOException, URISyntaxException {
|
public void testStruct2() throws IOException, URISyntaxException {
|
||||||
compileAndCompare("struct-2", log());
|
compileAndCompare("struct-2");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStruct1() throws IOException, URISyntaxException {
|
public void testStruct1() throws IOException, URISyntaxException {
|
||||||
compileAndCompare("struct-1", log());
|
compileAndCompare("struct-1");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStruct0() throws IOException, URISyntaxException {
|
public void testStruct0() throws IOException, URISyntaxException {
|
||||||
compileAndCompare("struct-0", log());
|
compileAndCompare("struct-0");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
14
src/test/kc/struct-err-2.kc
Normal file
14
src/test/kc/struct-err-2.kc
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Incompatible struct assignment
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
byte x;
|
||||||
|
byte y;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
struct Point p1;
|
||||||
|
p1 = 4;
|
||||||
|
const byte* SCREEN = 0x0400;
|
||||||
|
SCREEN[0] = p1.x;
|
||||||
|
}
|
||||||
|
|
17
src/test/kc/struct-err-3.kc
Normal file
17
src/test/kc/struct-err-3.kc
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Incompatible struct parameter
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
byte x;
|
||||||
|
byte y;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
print(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
const byte* SCREEN = 0x0400;
|
||||||
|
|
||||||
|
void print(struct Point p) {
|
||||||
|
*SCREEN = p.x;
|
||||||
|
}
|
||||||
|
|
@ -14,70 +14,7 @@ Replacing struct member reference (struct Point) main::p1.y with member variable
|
|||||||
Replacing struct member reference (struct Point) main::p1.x with member variable reference (byte) main::p1_x
|
Replacing struct member reference (struct Point) main::p1.x with member variable reference (byte) main::p1_x
|
||||||
Replacing struct member reference (struct Point) print::p.x with member variable reference (byte) print::p_x
|
Replacing struct member reference (struct Point) print::p.x with member variable reference (byte) print::p_x
|
||||||
Replacing struct member reference (struct Point) print::p.y with member variable reference (byte) print::p_y
|
Replacing struct member reference (struct Point) print::p.y with member variable reference (byte) print::p_y
|
||||||
SYMBOLS
|
|
||||||
(label) @1
|
|
||||||
(label) @2
|
|
||||||
(label) @begin
|
|
||||||
(label) @end
|
|
||||||
(byte) Point::x
|
|
||||||
(byte) Point::y
|
|
||||||
(byte*) SCREEN
|
|
||||||
(byte) idx
|
|
||||||
(void()) main()
|
|
||||||
(void~) main::$0
|
|
||||||
(void~) main::$1
|
|
||||||
(label) main::@return
|
|
||||||
(struct Point) main::p1
|
|
||||||
(byte) main::p1_x
|
|
||||||
(byte) main::p1_y
|
|
||||||
(void()) print((byte) print::p_x , (byte) print::p_y)
|
|
||||||
(label) print::@return
|
|
||||||
(struct Point) print::p
|
|
||||||
(byte) print::p_x
|
|
||||||
(byte) print::p_y
|
|
||||||
|
|
||||||
Adding pointer type conversion cast (byte*) SCREEN in (byte*) SCREEN ← (number) $400
|
Adding pointer type conversion cast (byte*) SCREEN in (byte*) SCREEN ← (number) $400
|
||||||
INITIAL CONTROL FLOW GRAPH
|
|
||||||
@begin: scope:[] from
|
|
||||||
to:@1
|
|
||||||
main: scope:[main] from
|
|
||||||
[0] (byte) main::p1_x ← (byte) 0
|
|
||||||
[1] (byte) main::p1_y ← (byte) 0
|
|
||||||
[2] (byte) main::p1_x ← (number) 1
|
|
||||||
[3] (byte) main::p1_y ← (number) 4
|
|
||||||
[4] (void~) main::$0 ← call print (byte) main::p1_x (byte) main::p1_y
|
|
||||||
[5] (byte) main::p1_x ← (number) 2
|
|
||||||
[6] (void~) main::$1 ← call print (byte) main::p1_x (byte) main::p1_y
|
|
||||||
to:main::@return
|
|
||||||
main::@return: scope:[main] from main
|
|
||||||
[7] return
|
|
||||||
to:@return
|
|
||||||
@1: scope:[] from @begin
|
|
||||||
[8] (byte*) SCREEN ← ((byte*)) (number) $400
|
|
||||||
[9] (byte) idx ← (number) 0
|
|
||||||
to:@2
|
|
||||||
print: scope:[print] from
|
|
||||||
[10] *((byte*) SCREEN + (byte) idx) ← (byte) print::p_x
|
|
||||||
[11] (byte) idx ← ++ (byte) idx
|
|
||||||
[12] *((byte*) SCREEN + (byte) idx) ← (byte) print::p_y
|
|
||||||
[13] (byte) idx ← ++ (byte) idx
|
|
||||||
to:print::@return
|
|
||||||
print::@return: scope:[print] from print
|
|
||||||
[14] return
|
|
||||||
to:@return
|
|
||||||
@2: scope:[] from @1
|
|
||||||
[15] call main
|
|
||||||
to:@end
|
|
||||||
@end: scope:[] from @2
|
|
||||||
|
|
||||||
Eliminating unused variable - keeping the call (void~) main::$0
|
|
||||||
Eliminating unused variable - keeping the call (void~) main::$1
|
|
||||||
PROCEDURE MODIFY VARIABLE ANALYSIS
|
|
||||||
main modifies idx
|
|
||||||
print modifies idx
|
|
||||||
|
|
||||||
Completing Phi functions...
|
|
||||||
Completing Phi functions...
|
|
||||||
|
|
||||||
CONTROL FLOW GRAPH SSA
|
CONTROL FLOW GRAPH SSA
|
||||||
@begin: scope:[] from
|
@begin: scope:[] from
|
||||||
|
Loading…
Reference in New Issue
Block a user