Merge pull request 'assign-return-break-to-anything' (#56) from assign-return-break-to-anything into main
Reviewed-on: #56
This commit is contained in:
commit
f17dba23ce
@ -42,7 +42,7 @@ func primitiveType (name string) *entity.TypeNamed {
|
||||
func init () {
|
||||
builtinTypes["Index"] = &entity.TypeWord { Acc: entity.AccessPublic }
|
||||
builtinTypes["Byte"] = &entity.TypeInt { Acc: entity.AccessPublic, Signed: false, Width: 8 }
|
||||
builtinTypes["Bool"] = &entity.TypeInt { Acc: entity.AccessPublic, Signed: false, Width: 8 }
|
||||
builtinTypes["Bool"] = &entity.TypeInt { Acc: entity.AccessPublic, Signed: false, Width: 1 }
|
||||
builtinTypes["Rune"] = &entity.TypeInt { Acc: entity.AccessPublic, Signed: false, Width: 32 }
|
||||
builtinTypes["String"] = &entity.TypeSlice {
|
||||
Acc: entity.AccessPublic,
|
||||
|
@ -23,6 +23,16 @@ U: (| Int F64)
|
||||
`)
|
||||
}
|
||||
|
||||
func TestMatchReturnValueUsed (test *testing.T) {
|
||||
testString (test,
|
||||
`
|
||||
U: (| Int F64)
|
||||
[isInt u:U]:Bool = match u in
|
||||
| u:Int [return true]
|
||||
| u:F64 false
|
||||
`)
|
||||
}
|
||||
|
||||
func TestMatchUnionUnderComplete (test *testing.T) {
|
||||
testString (test,
|
||||
`
|
||||
@ -79,3 +89,23 @@ U: (| Int F64 UInt)
|
||||
| u:Int u
|
||||
`)
|
||||
}
|
||||
|
||||
func TestIfElseReturnValueUsed (test *testing.T) {
|
||||
testString (test,
|
||||
`
|
||||
[is5 x:Int]:Bool = if [= x 5]
|
||||
then true
|
||||
else [return false]
|
||||
`)
|
||||
}
|
||||
|
||||
func TestIfElseBreakValueUsed (test *testing.T) {
|
||||
testString (test,
|
||||
`
|
||||
[f x:Int]: Int = loop {
|
||||
y:Int = if [= x 5]
|
||||
then [break 0]
|
||||
else x
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
@ -816,12 +816,6 @@ func (this *Tree) analyzeBreak (
|
||||
entity.Expression,
|
||||
error,
|
||||
) {
|
||||
if into != nil {
|
||||
return nil, errors.Errorf (
|
||||
brk.Position, "expected %v",
|
||||
entity.FormatType(into))
|
||||
}
|
||||
|
||||
loop, ok := this.topLoop()
|
||||
if !ok {
|
||||
return nil, errors.Errorf (
|
||||
@ -853,12 +847,6 @@ func (this *Tree) analyzeReturn (
|
||||
entity.Expression,
|
||||
error,
|
||||
) {
|
||||
if into != nil {
|
||||
return nil, errors.Errorf (
|
||||
ret.Position, "expected %v",
|
||||
entity.FormatType(into))
|
||||
}
|
||||
|
||||
ret.Declaration, _ = this.topDeclaration()
|
||||
var ty entity.Type
|
||||
switch ret.Declaration.(type) {
|
||||
|
@ -81,3 +81,13 @@ testUnit (test,
|
||||
"", "F64\n",
|
||||
0,
|
||||
)}
|
||||
|
||||
func TestReturnAssign (test *testing.T) {
|
||||
dependencies := []string {
|
||||
compileDependency(test, "io"),
|
||||
}
|
||||
testUnit (test,
|
||||
"/test-data/data/returnassign", dependencies,
|
||||
"", "false\n",
|
||||
0,
|
||||
)}
|
||||
|
2
compiler/test-data/data/returnassign/fspl.mod
Normal file
2
compiler/test-data/data/returnassign/fspl.mod
Normal file
@ -0,0 +1,2 @@
|
||||
'678748d4-ce9e-45db-bccb-07eecf067770'
|
||||
+ 'io'
|
10
compiler/test-data/data/returnassign/main.fspl
Normal file
10
compiler/test-data/data/returnassign/main.fspl
Normal file
@ -0,0 +1,10 @@
|
||||
[main]:I32 'main' = {
|
||||
if [is5 7]
|
||||
then io::[println 'true']
|
||||
else io::[println 'false']
|
||||
0
|
||||
}
|
||||
|
||||
[is5 x:Int]:Bool = if [= x 5]
|
||||
then true
|
||||
else [return false]
|
@ -67,7 +67,8 @@ chunks of memory, and to index arrays and such.
|
||||
Byte is defined as the smallest addressable integer. It is unsigned. It is
|
||||
usually equivalent to U8.
|
||||
### Bool
|
||||
Bool is a boolean type. It is equivalent to Byte.
|
||||
Bool is a boolean type. It is equivalent to U1. For now, since arbitrary width
|
||||
integers are not supported, it is the only way to get a U1 type.
|
||||
### Rune
|
||||
Rune is defined as a U32. It represents a single UTF-32 code point.
|
||||
### String
|
||||
@ -266,13 +267,15 @@ assignment rules of all of its break statements. Loops may be nested, and break
|
||||
statements only apply to the closest containing loop. The value of the loop's
|
||||
expression is never used.
|
||||
### Break
|
||||
Break allows breaking out of loops. It has no value and may not be assigned to
|
||||
anything.
|
||||
Break allows breaking out of loops. It may be assigned to anything, but the
|
||||
assignment will have no effect as execution of the code after and surrounding it
|
||||
will cease.
|
||||
### Return
|
||||
Return allows terminating functions before they have reached their end. It
|
||||
accepts values that may be assigned to the function's return type. If a function
|
||||
does not return anything, the return statement does not accept a value. In all
|
||||
cases, return statements have no value and may not be assigned to anything.
|
||||
does not return anything, the return statement does not accept a value. It may
|
||||
be assigned to anything, but the assignment will have no effect as execution of
|
||||
the function or method will cease.
|
||||
### Assignment
|
||||
Assignment allows assigning the result of one expression to one or more location
|
||||
expressions. The assignment expression itself has no value and may not be
|
||||
|
@ -200,7 +200,7 @@ U: (| Int F64)
|
||||
|
||||
func TestMatchReturn (test *testing.T) {
|
||||
testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i8
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::U" = type { i64, i64 }
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::isInt"(%"0zNZN147MN2wzMAQ6NS2dQ==::U" %u) {
|
||||
0:
|
||||
@ -230,6 +230,45 @@ U: (| Int F64)
|
||||
`)
|
||||
}
|
||||
|
||||
func TestMatchReturnValueUsed (test *testing.T) {
|
||||
testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::U" = type { i64, i64 }
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::isInt"(%"0zNZN147MN2wzMAQ6NS2dQ==::U" %u) {
|
||||
0:
|
||||
%1 = alloca %"0zNZN147MN2wzMAQ6NS2dQ==::U"
|
||||
store %"0zNZN147MN2wzMAQ6NS2dQ==::U" %u, ptr %1
|
||||
%2 = alloca i64
|
||||
%3 = alloca double
|
||||
%4 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::U", ptr %1, i32 0, i32 0
|
||||
%5 = load i64, ptr %4
|
||||
switch i64 %5, label %6 [
|
||||
i64 7620767046192759206, label %8
|
||||
i64 9186060094042213285, label %11
|
||||
]
|
||||
6:
|
||||
%7 = phi i1 [ false, %11 ], [ zeroinitializer, %0 ]
|
||||
ret i1 %7
|
||||
8:
|
||||
%9 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::U", ptr %1, i32 0, i32 1
|
||||
%10 = load i64, ptr %9
|
||||
store i64 %10, ptr %2
|
||||
ret i1 true
|
||||
11:
|
||||
%12 = getelementptr %"0zNZN147MN2wzMAQ6NS2dQ==::U", ptr %1, i32 0, i32 1
|
||||
%13 = load double, ptr %12
|
||||
store double %13, ptr %3
|
||||
br label %6
|
||||
}
|
||||
`,
|
||||
`
|
||||
U: (| Int F64)
|
||||
[isInt u:U]:Bool = match u in
|
||||
| u:Int [return true]
|
||||
| u:F64 false
|
||||
`)
|
||||
}
|
||||
|
||||
func TestMatchUnionUnderComplete (test *testing.T) {
|
||||
testString (test,
|
||||
`%"0zNZN147MN2wzMAQ6NS2dQ==::U" = type { i64, i64 }
|
||||
@ -298,3 +337,63 @@ U: (| Int F64 UInt)
|
||||
| u:F64 [print 'F64']
|
||||
`)
|
||||
}
|
||||
|
||||
func TestIfElseReturnValueUsed (test *testing.T) {
|
||||
testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1
|
||||
define %"AAAAAAAAAAAAAAAAAAAAAA==::Bool" @"0zNZN147MN2wzMAQ6NS2dQ==::is5"(i64 %x) {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
store i64 %x, ptr %1
|
||||
%2 = load i64, ptr %1
|
||||
%3 = icmp eq i64 %2, 5
|
||||
br i1 %3, label %4, label %7
|
||||
4:
|
||||
br label %5
|
||||
5:
|
||||
%6 = phi i1 [ true, %4 ]
|
||||
ret i1 %6
|
||||
7:
|
||||
ret i1 false
|
||||
}
|
||||
`,
|
||||
`
|
||||
[is5 x:Int]:Bool = if [= x 5]
|
||||
then true
|
||||
else [return false]
|
||||
`)
|
||||
}
|
||||
|
||||
func TestIfElseBreakValueUsed (test *testing.T) {
|
||||
testString (test,
|
||||
`define i64 @"0zNZN147MN2wzMAQ6NS2dQ==::f"(i64 %x) {
|
||||
0:
|
||||
%1 = alloca i64
|
||||
store i64 %x, ptr %1
|
||||
%2 = alloca i64
|
||||
br label %3
|
||||
3:
|
||||
%4 = load i64, ptr %1
|
||||
%5 = icmp eq i64 %4, 5
|
||||
br i1 %5, label %6, label %9
|
||||
6:
|
||||
br label %11
|
||||
7:
|
||||
%8 = phi i64 [ %10, %9 ]
|
||||
store i64 %8, ptr %2
|
||||
br label %3
|
||||
9:
|
||||
%10 = load i64, ptr %1
|
||||
br label %7
|
||||
11:
|
||||
ret i64 0
|
||||
}
|
||||
`,
|
||||
`
|
||||
[f x:Int]: Int = loop {
|
||||
y:Int = if [= x 5]
|
||||
then [break 0]
|
||||
else x
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ func (this *generator) generateIfElse (ifelse *entity.IfElse, mode resultMode) (
|
||||
var trueBlock, falseBlock *llvm.Block
|
||||
var tru, fals llvm.Value
|
||||
var loc bool
|
||||
var irIncomings []*llvm.Incoming
|
||||
|
||||
trueBlock = this.blockManager.newBlock()
|
||||
exitBlock := this.blockManager.newBlock()
|
||||
@ -56,7 +57,15 @@ func (this *generator) generateIfElse (ifelse *entity.IfElse, mode resultMode) (
|
||||
|
||||
tru, loc, err = this.generateExpression(ifelse.True, mode)
|
||||
if err != nil { return nil, false, err }
|
||||
if !this.blockManager.Terminated() { this.blockManager.NewBr(exitBlock) }
|
||||
if !this.blockManager.Terminated() {
|
||||
if tru != nil {
|
||||
irIncomings = append(irIncomings, &llvm.Incoming {
|
||||
X: tru,
|
||||
Predecessor: this.blockManager.Block,
|
||||
})
|
||||
}
|
||||
this.blockManager.NewBr(exitBlock)
|
||||
}
|
||||
|
||||
if ifelse.False == nil {
|
||||
// there is no false case
|
||||
@ -66,9 +75,22 @@ func (this *generator) generateIfElse (ifelse *entity.IfElse, mode resultMode) (
|
||||
} else {
|
||||
// there is a false case
|
||||
falseBlock = this.blockManager.newBlock()
|
||||
fals, _, err = this.generateExpression(ifelse.False, mode)
|
||||
thisMode := mode
|
||||
if mode != resultModeAny {
|
||||
if loc { thisMode = resultModeLoc
|
||||
} else { thisMode = resultModeVal }
|
||||
}
|
||||
fals, _, err = this.generateExpression(ifelse.False, thisMode)
|
||||
if err != nil { return nil, false, err }
|
||||
if !this.blockManager.Terminated() { this.blockManager.NewBr(exitBlock) }
|
||||
if !this.blockManager.Terminated() {
|
||||
if fals != nil {
|
||||
irIncomings = append(irIncomings, &llvm.Incoming {
|
||||
X: fals,
|
||||
Predecessor: this.blockManager.Block,
|
||||
})
|
||||
}
|
||||
this.blockManager.NewBr(exitBlock)
|
||||
}
|
||||
|
||||
if mode == resultModeAny {
|
||||
// discard results of statements
|
||||
@ -78,18 +100,9 @@ func (this *generator) generateIfElse (ifelse *entity.IfElse, mode resultMode) (
|
||||
} else {
|
||||
// obtain results of statements
|
||||
// set up phi to capture results
|
||||
trueIncoming := &llvm.Incoming {
|
||||
X: tru,
|
||||
Predecessor: trueBlock,
|
||||
}
|
||||
falseIncoming := &llvm.Incoming {
|
||||
X: fals,
|
||||
Predecessor: falseBlock,
|
||||
}
|
||||
|
||||
previous.NewCondBr(condition, trueBlock, falseBlock)
|
||||
this.blockManager.Block = exitBlock
|
||||
return this.blockManager.NewPhi(trueIncoming, falseIncoming), loc, nil
|
||||
return this.blockManager.NewPhi(irIncomings...), loc, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -105,8 +118,8 @@ func (this *generator) generateMatch (match *entity.Match, mode resultMode) (llv
|
||||
exitBlock := this.blockManager.newBlock()
|
||||
irHashType := llvm.I64
|
||||
|
||||
irCases := make([]*llvm.Case, len(match.Cases))
|
||||
irIncomings := make([]*llvm.Incoming, len(match.Cases))
|
||||
irCases := make([]*llvm.Case, len(match.Cases))
|
||||
irIncomings := []*llvm.Incoming { }
|
||||
for index, cas := range match.Cases {
|
||||
// set up ir case
|
||||
caseBlock := this.blockManager.newBlock()
|
||||
@ -124,7 +137,7 @@ func (this *generator) generateMatch (match *entity.Match, mode resultMode) (llv
|
||||
this.blockManager.addDeclaration(cas.Declaration, data)
|
||||
|
||||
// generate case expression
|
||||
thisMode := mode // FIXME this logic isnt in if/else, why??
|
||||
thisMode := mode
|
||||
if index != 0 && mode != resultModeAny {
|
||||
if loc { thisMode = resultModeLoc
|
||||
} else { thisMode = resultModeVal }
|
||||
@ -132,11 +145,15 @@ func (this *generator) generateMatch (match *entity.Match, mode resultMode) (llv
|
||||
caseExpression, thisLoc, err := this.generateExpression(cas.Expression, thisMode)
|
||||
if err != nil { return nil, false, err }
|
||||
if index == 0 { loc = thisLoc }
|
||||
irIncomings[index] = &llvm.Incoming {
|
||||
X: caseExpression,
|
||||
Predecessor: caseBlock,
|
||||
if !this.blockManager.Terminated() {
|
||||
if caseExpression != nil {
|
||||
irIncomings = append(irIncomings, &llvm.Incoming {
|
||||
X: caseExpression,
|
||||
Predecessor: this.blockManager.Block,
|
||||
})
|
||||
}
|
||||
this.blockManager.NewBr(exitBlock)
|
||||
}
|
||||
if !this.blockManager.Terminated() { this.blockManager.NewBr(exitBlock) }
|
||||
}
|
||||
|
||||
// create switch branch
|
||||
|
@ -93,7 +93,7 @@ testString (test,
|
||||
|
||||
func TestCompare (test *testing.T) {
|
||||
testString (test,
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i8
|
||||
`%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::A" = type i64
|
||||
define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
|
||||
0:
|
||||
|
@ -9,7 +9,7 @@ testString (test,
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::Pegasus" = type { ptr, ptr, ptr, ptr, ptr }
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::Point" = type { i64, i64 }
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::Rectangle" = type { %"0zNZN147MN2wzMAQ6NS2dQ==::Point", %"0zNZN147MN2wzMAQ6NS2dQ==::Point" }
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i8
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::Bool" = type i1
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::Byte" = type i8
|
||||
%"AAAAAAAAAAAAAAAAAAAAAA==::Rune" = type i32
|
||||
%"0zNZN147MN2wzMAQ6NS2dQ==::AllInts" = type { %"AAAAAAAAAAAAAAAAAAAAAA==::Bool", %"AAAAAAAAAAAAAAAAAAAAAA==::Byte", %"AAAAAAAAAAAAAAAAAAAAAA==::Index", %"AAAAAAAAAAAAAAAAAAAAAA==::Rune", i64, i64, i8, i16, i32, i64, i8, i16, i32, i64 }
|
||||
|
Loading…
Reference in New Issue
Block a user