Skip to content

Commit 7d98adc

Browse files
supercooldaveEliasC
supercooldave
authored andcommitted
Modified type of return for better programmer experience (#827)
* Modified return's type for better programmer experience * Loosened rules for Bottom type. * Fixed problem in test case * Actually include new test * Improve error reporting when matching on Nothing or null
1 parent f3d3519 commit 7d98adc

File tree

12 files changed

+130
-12
lines changed

12 files changed

+130
-12
lines changed

src/tests/encore/match/nothing.enc

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
active class Main
2+
def main(args : [String]) : unit
3+
match Nothing with
4+
case Nothing =>
5+
println("Until the typechecker gets clever enough, this won't compile")
6+
end
7+
end
8+
end
9+
end

src/tests/encore/match/nothing.fail

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Not enough information to infer the type.

src/tests/encore/return/return.enc

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ active class Main
99
end
1010
def c() : unit
1111
let
12-
x = return(())
12+
x : unit = return(())
1313
in
1414
()
1515
end

src/tests/encore/return/return2.enc

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
read class Config
3+
def notbroke() : int
4+
if true then
5+
return 10
6+
else
7+
return 11
8+
end
9+
end
10+
11+
def fixed() : int
12+
match Nothing : Maybe[int] with
13+
case Nothing => return 10
14+
case Just(ice) => return 11
15+
end
16+
end
17+
18+
-- works
19+
def other(x : bool) : int
20+
match x with
21+
case true => return 10
22+
case false => return 11
23+
end
24+
end
25+
end
26+
27+
active class Main
28+
def main() : unit
29+
println("{}",(new Config).notbroke())
30+
end
31+
end

src/tests/encore/return/return2.out

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
10

src/tests/encore/return/return3.enc

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
active class Main
3+
def skip() : unit
4+
println("skip")
5+
end
6+
7+
def baz() : int
8+
if false then
9+
return 10
10+
else
11+
this.skip()
12+
end
13+
10
14+
end
15+
16+
def main() : unit
17+
println("{}", this.baz())
18+
end
19+
20+
def bar() : String
21+
"bar"
22+
end
23+
24+
def foo() : Maybe[int]
25+
if true then
26+
return Nothing : Maybe[int]
27+
else
28+
this.bar()
29+
end
30+
Just(11)
31+
end
32+
end

src/tests/encore/return/return3.out

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
skip
2+
10

src/tests/encore/return/return4.enc

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
active class Main
3+
def skip() : unit
4+
println("skip")
5+
end
6+
7+
def baz() : int
8+
if false then
9+
return 10
10+
else
11+
this.skip()
12+
end
13+
10
14+
end
15+
16+
def main() : unit
17+
println("{}", this.baz())
18+
end
19+
20+
def bar() : String
21+
"bar"
22+
end
23+
24+
def foo() : Maybe[int]
25+
if true then
26+
return Nothing
27+
else
28+
this.bar()
29+
end
30+
Just(11)
31+
end
32+
end

src/tests/encore/return/return4.out

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
skip
2+
10

src/types/Typechecker/TypeError.hs

+2-1
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,8 @@ instance Show Error where
511511
printf "Unbound function variable '%s'" (show name)
512512
show (NonFunctionTypeError ty) =
513513
printf "Cannot use value of type '%s' as a function" (show ty)
514-
show BottomTypeInferenceError = "Cannot infer type of 'Nothing'"
514+
show BottomTypeInferenceError = "Not enough information to infer the type.\n" ++
515+
"Try adding more type information."
515516
show IfInferenceError = "Cannot infer result type of if-statement"
516517
show (IfBranchMismatchError ty1 ty2) =
517518
"Type mismatch in different branches of if-statement:\n" ++

src/types/Typechecker/Typechecker.hs

+15-9
Original file line numberDiff line numberDiff line change
@@ -1016,10 +1016,9 @@ instance Checkable Expr where
10161016
when (null clauses) $
10171017
tcError EmptyMatchClauseError
10181018
eArg <- typecheck arg
1019+
checkMatchArgument eArg
10191020
let argType = AST.getType eArg
1020-
when (isActiveSingleType argType) $
1021-
unless (isThisAccess arg) $
1022-
tcError ActiveMatchError
1021+
10231022
eClauses <- mapM (checkClause argType) clauses
10241023
checkForPrivateExtractors eArg (map mcpattern eClauses)
10251024
resultType <- checkAllHandlersSameType eClauses
@@ -1028,6 +1027,16 @@ instance Checkable Expr where
10281027
eClauses' = map updateClauseType eClauses
10291028
return $ setType resultType match {arg = eArg, clauses = eClauses'}
10301029
where
1030+
checkMatchArgument arg = do
1031+
let argType = AST.getType arg
1032+
when (isActiveSingleType argType) $
1033+
unless (isThisAccess arg) $
1034+
tcError ActiveMatchError
1035+
when (any isBottomType (typeComponents argType)) $
1036+
pushError arg BottomTypeInferenceError
1037+
when (any isNullType (typeComponents argType)) $
1038+
pushError arg NullTypeInferenceError
1039+
10311040
checkForPrivateExtractors arg = mapM (checkForPrivateExtractor arg)
10321041

10331042
checkForPrivateExtractor matchArg p@ExtractorPattern{name, arg} = do
@@ -1288,7 +1297,7 @@ instance Checkable Expr where
12881297
-- E |- expr : t
12891298
-- E |- currentMethod : _ -> t
12901299
-- -----------------------------
1291-
-- E |- return expr : t
1300+
-- E |- return expr : _|_
12921301
doTypecheck ret@(Return {val}) =
12931302
do eVal <- typecheck val
12941303
context <- asks currentExecutionContext
@@ -1302,7 +1311,7 @@ instance Checkable Expr where
13021311
unlessM (eType `subtypeOf` ty) $
13031312
pushError ret $ ExpectingOtherTypeError
13041313
(show ty ++ " (type of the enclosing method or function)") eType
1305-
return $ setType eType ret {val = eVal}
1314+
return $ setType bottomType ret {val = eVal}
13061315

13071316
-- isStreaming(currentMethod)
13081317
-- ----------------------------
@@ -2009,10 +2018,7 @@ coercedInto actual expected
20092018
unless (canBeNull expected) $
20102019
tcError $ CannotBeNullError expected
20112020
return expected
2012-
| isBottomType actual = do
2013-
when (any isBottomType $ typeComponents expected) $
2014-
tcError BottomTypeInferenceError
2015-
return expected
2021+
| isBottomType actual = return expected
20162022
| isBottomType expected =
20172023
tcError BottomTypeInferenceError
20182024
| otherwise = do

src/types/Typechecker/Util.hs

+2-1
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ isUnifiableWith ty types
554554
tyUniable <- typeIsUnifiable ty
555555
tysUniable <- allM typeIsUnifiable types
556556
return $ tyUniable && tysUniable &&
557-
not (isNullType ty) && not (isBottomType ty)
557+
not (isNullType ty)
558558

559559
unifyTypes :: [Type] -> TypecheckM (Maybe Type)
560560
unifyTypes tys = do
@@ -611,6 +611,7 @@ doUnifyTypes inter args@(ty:tys)
611611

612612
uniquifyTypeVars :: [Type] -> Type -> TypecheckM Type
613613
uniquifyTypeVars params = typeMapM (uniquifyTypeVar params)
614+
614615
uniquifyTypeVar :: [Type] -> Type -> TypecheckM Type
615616
uniquifyTypeVar params ty
616617
| isTypeVar ty = do

0 commit comments

Comments
 (0)