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 PassNTypeInference(program).execute();
|
||||
new PassNTypeIdSimplification(program).execute();
|
||||
|
||||
new Pass1AssertProcedureCallParameters(program).execute();
|
||||
new Pass1UnwindStructValues(program).execute();
|
||||
|
||||
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<>();
|
||||
boolean procedureUnwound = false;
|
||||
for(Variable parameter : procedure.getParameters()) {
|
||||
@ -72,8 +73,7 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Unwind all references to full structs
|
||||
// Unwind all references to struct values into members
|
||||
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
|
||||
while(stmtIt.hasNext()) {
|
||||
@ -83,44 +83,7 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
||||
if(assignment.getlValue() instanceof VariableRef) {
|
||||
Variable assignedVar = getScope().getVariable((VariableRef) assignment.getlValue());
|
||||
if(assignedVar.getType() instanceof SymbolTypeStruct) {
|
||||
// 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(), 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);
|
||||
}
|
||||
}
|
||||
unwindStructAssignment(assignment, assignedVar, stmtIt, structUnwinding);
|
||||
}
|
||||
}
|
||||
} else if(statement instanceof StatementCall) {
|
||||
@ -179,6 +142,56 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
||||
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.
|
||||
*/
|
||||
|
@ -51,43 +51,53 @@ public class TestPrograms {
|
||||
}
|
||||
|
||||
@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");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testError0() throws IOException, URISyntaxException {
|
||||
public void testStructError0() throws IOException, URISyntaxException {
|
||||
assertError("struct-err-0", "Unknown struct type");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStruct5() throws IOException, URISyntaxException {
|
||||
compileAndCompare("struct-5", log());
|
||||
compileAndCompare("struct-5");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStruct4() throws IOException, URISyntaxException {
|
||||
compileAndCompare("struct-4", log());
|
||||
compileAndCompare("struct-4");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStruct3() throws IOException, URISyntaxException {
|
||||
compileAndCompare("struct-3", log().verboseCreateSsa());
|
||||
compileAndCompare("struct-3");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStruct2() throws IOException, URISyntaxException {
|
||||
compileAndCompare("struct-2", log());
|
||||
compileAndCompare("struct-2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStruct1() throws IOException, URISyntaxException {
|
||||
compileAndCompare("struct-1", log());
|
||||
compileAndCompare("struct-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStruct0() throws IOException, URISyntaxException {
|
||||
compileAndCompare("struct-0", log());
|
||||
compileAndCompare("struct-0");
|
||||
}
|
||||
|
||||
@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) 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
|
||||
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
|
||||
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
|
||||
@begin: scope:[] from
|
||||
|
Loading…
Reference in New Issue
Block a user