From 280ef65cf8297d07bb60ecc18b7c5daadbecbda5 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Tue, 22 Oct 2024 10:43:46 +0300 Subject: [PATCH] sema: improve return statement analysis --- std/jule/build/log.jule | 3 +- std/jule/sema/fn.jule | 9 ++- std/jule/sema/sema.jule | 6 +- std/jule/sema/type2.jule | 137 ++++++++++++++++++--------------------- 4 files changed, 76 insertions(+), 79 deletions(-) diff --git a/std/jule/build/log.jule b/std/jule/build/log.jule index 8ad53da5f..5b4bd0109 100644 --- a/std/jule/build/log.jule +++ b/std/jule/build/log.jule @@ -244,7 +244,8 @@ enum LogMsg: str { InvalidTypeForComptimeIter: `type @ is not supports comptime iterations`, InvalidComptimeIter: `comptime iterations can only be range iteration`, InvalidComptimeTypeMatchExpr: `comptime type-match expressions can take only type declarations`, - WrongRetForward: "function return forwaring is wrong\n want (@)\n have (@)", + NotEnoughVariablesForRet: "not enough variables for return\n want (@)\n have (@)", + TooManyVariablesForRet: "too many variables for return\n want (@)\n have (@)", ExportedUsedAsAnonymous: `define @ is exported for backend so you cannot use as anonymous function`, InvalidImportPath: `invalid import path: @`, AutoAliasFail: `import path is not suitable for auto-aliasing: @`, diff --git a/std/jule/sema/fn.jule b/std/jule/sema/fn.jule index 70b3b19ea..860178c23 100644 --- a/std/jule/sema/fn.jule +++ b/std/jule/sema/fn.jule @@ -102,7 +102,14 @@ impl Fn { // Reports whether function has return variable(s). fn AnyVar(self): bool { - ret self.Result != nil && len(self.Result.Idents) > 0 + if self.Result != nil { + for _, ident in self.Result.Idents { + if !token::IsAnonIdent(ident.Kind) { + ret true + } + } + } + ret false } // Force to new instance. diff --git a/std/jule/sema/sema.jule b/std/jule/sema/sema.jule index 69e1d477a..e8d85264f 100644 --- a/std/jule/sema/sema.jule +++ b/std/jule/sema/sema.jule @@ -1406,7 +1406,7 @@ impl sema { ret } - fn checkFnDeclResultDup(mut self, &f: &Fn): (ok: bool) { + fn checkFnDeclResultDup(mut self, mut &f: &Fn): (ok: bool) { ok = true if f.IsVoid() { ret @@ -1431,12 +1431,12 @@ impl sema { } // Lookup in return identifiers. - itself_lookup: + itselfLookup: for j, jv in f.Result.Idents { match { | j >= i: // Skip current and following identifiers. - break itself_lookup + break itselfLookup | jv.Kind == v.Kind: goto exist } diff --git a/std/jule/sema/type2.jule b/std/jule/sema/type2.jule index 199d6417e..0ace0a3de 100644 --- a/std/jule/sema/type2.jule +++ b/std/jule/sema/type2.jule @@ -1263,8 +1263,9 @@ impl rangeChecker { struct retTypeChecker { sc: &scopeChecker f: &FnIns - types: []&Type // Return types. - exprs: []&ast::Expr // Return expressions. + types: []&Type // Return types. + exprs: []&Data // Return expressions. + exprDecls: []&ast::Expr errorToken: &token::Token model: ExprModel mutable: bool // Whether the return expression should be mutable. @@ -1277,82 +1278,67 @@ impl retTypeChecker { } } - fn prepareExprs(mut self, mut &e: &ast::Expr) { + fn prepareExprs(mut self, mut &e: &ast::Expr): (ok: bool) { if e == nil { - ret + ret true } match type e.Kind { | &ast::TupleExpr: - self.exprs = (&ast::TupleExpr)(e.Kind).Expr + self.exprDecls = (&ast::TupleExpr)(e.Kind).Expr |: - self.exprs = append(self.exprs, e) + self.exprDecls = [e] } - } - - fn checkExprs(mut self) { - if len(self.exprs) == 0 { - ret - } - mut datas := make([]&Data, 0, len(self.exprs)) + ok = true mut eval := self.sc.s.eval(self.sc) eval.target.mutable = self.mutable - for (i, mut expr) in self.exprs { - if i >= len(self.types) { - break + for (i, mut expr) in self.exprDecls { + if i < len(self.types) { + eval.prefix = self.types[i] } - - mut t := self.types[i] - - eval.prefix = t mut d := eval.evalExpr(expr) - if d == nil { - continue + ok = ok && d != nil + if ok { + self.exprs = append(self.exprs, d) } + } + ret + } - datas = append(datas, d) - + fn checkExprs(mut self) { + if len(self.exprs) == 0 { + ret + } + for (i, mut d) in self.exprs { + mut expr := self.exprDecls[i] if self.mutable && !d.Mutable && d.Type.Mutable() { - self.sc.s.pushErr(self.errorToken, build::LogMsg.RetWithMutTypedNonMut) + self.sc.s.pushErr(expr.Token, build::LogMsg.RetWithMutTypedNonMut) ret } - mut ac := assignTypeChecker{ s: self.sc.s, - dest: t, + dest: self.types[i], d: d, - errorToken: self.errorToken, + errorToken: expr.Token, refers: self.sc.getHardRoot().owner.Refers, } ac.check() } // Set Model. - if len(datas) > 1 { // Tuple - self.model = &TupleExprModel{ - Datas: datas, - } - } else if len(datas) == 1 { - self.model = datas[0].Model + if len(self.exprs) > 1 { // Tuple + self.model = &TupleExprModel{Datas: self.exprs} + } else if len(self.exprs) == 1 { + self.model = self.exprs[0].Model } } - // Assumes len(self.exprs) == 0 and kind is [&ast::FnCallExpr]. - fn tryFuncMultiRetForward(mut self): bool { - mut eval := self.sc.s.eval(self.sc) - mut d := eval.evalExpr(self.exprs[0]) - if d == nil { - // Return true to skip following "false" handling errors. - // Evaluation error is enough for developer for now. - ret true - } - mut tup := d.Type.Tup() - if tup == nil { - ret false - } + // Assumes the len(self.exprs) == 1 and self.exprs[0].Type.Tup() != nil. + fn tryFuncMultiRetForward(mut self) { + mut tup := self.exprs[0].Type.Tup() if len(tup.Types) != len(self.types) { goto err } - self.model = d.Model + self.model = self.exprs[0].Model for i in self.types { mut t1 := self.types[i] mut t2 := tup.Types[i] @@ -1360,8 +1346,12 @@ impl retTypeChecker { goto err } } - ret true + ret err: + self.notFitVariables() + } + + fn notFitVariables(mut self) { mut wanted := strings::Builder.New(1 << 5) for i, t in self.types { wanted.WriteStr(t.Str())! @@ -1370,43 +1360,42 @@ impl retTypeChecker { } } mut given := strings::Builder.New(1 << 5) - for i, t in tup.Types { - given.WriteStr(t.Str())! - if len(self.types)-i > 1 { + for i, d in self.exprs { + given.WriteStr(d.Type.Str())! + if len(self.exprs)-i > 1 { given.WriteStr(", ")! } } - self.sc.s.pushErr(self.errorToken, build::LogMsg.WrongRetForward, wanted.Str(), given.Str()) - ret false + if len(self.exprs) < len(self.types) { + self.sc.s.pushErr(self.errorToken, build::LogMsg.NotEnoughVariablesForRet, wanted.Str(), given.Str()) + } else { + self.sc.s.pushErr(self.errorToken, build::LogMsg.TooManyVariablesForRet, wanted.Str(), given.Str()) + } } fn check(mut self, mut &e: &ast::Expr): bool { self.prepareTypes() - self.prepareExprs(e) + mut ok := self.prepareExprs(e) + if !ok { + ret false + } + + // Check whether we have enough variables for return. match { - | len(self.exprs) == 0 && len(self.types) > 0: - if !self.f.Decl.AnyVar() { - self.sc.s.pushErr(self.errorToken, build::LogMsg.RequireRetExpr) - ret false - } + | len(self.exprs) == 0 && len(self.types) > 0 && self.f.Decl.AnyVar(): ret true - | len(self.exprs) > 0 && self.f != nil && self.f.Decl.IsVoid(): - self.sc.s.pushErr(self.errorToken, build::LogMsg.VoidFnRetExpr) - ret false - | len(self.exprs) == 1 && len(self.types) > 1: - match type self.exprs[0].Kind { - | &ast::FnCallExpr: - if self.tryFuncMultiRetForward() { - ret true - } + | len(self.exprs) == 1 && len(self.types) > 1 && self.exprs[0].Type.Tup() != nil: + match type self.exprs[0].Model { + | &FnCallExprModel: + self.tryFuncMultiRetForward() + ret true } - self.sc.s.pushErr(self.errorToken, build::LogMsg.MissingMultiRet) + fall + | len(self.exprs) != len(self.types): + self.notFitVariables() ret false - | len(self.exprs) < len(self.types): - self.sc.s.pushErr(self.errorToken, build::LogMsg.MissingMultiRet) - | len(self.exprs) > len(self.types): - self.sc.s.pushErr(self.errorToken, build::LogMsg.OverflowRet) } + self.checkExprs() ret true }