diff --git a/src/julec/opt/boundary.jule b/src/julec/opt/boundary.jule new file mode 100644 index 000000000..a3071aea8 --- /dev/null +++ b/src/julec/opt/boundary.jule @@ -0,0 +1,191 @@ +// Copyright 2024 The Jule Programming Language. +// Use of this source code is governed by a BSD 3-Clause +// license that can be found in the LICENSE file. + +use std::jule::constant::{Const} +use std::jule::lex::{TokenId} +use std::jule::sema::{Var, StructSubIdentExprModel, UnaryExprModel, ExprModel, TypeKind} + +const invalidBoundary = 0x0 + +struct boundaryVar { + var: uintptr + maxSize: []ExprModel +} + +// Information wrapper for boundary analysis. +struct boundary { + vars: []&boundaryVar +} + +impl boundary { + // Appends variable with initial maximum size expression. + // If variable is already exist, updates maximum size information. + fn pushVar(mut self, var: uintptr, mut maxSize: ExprModel) { + if !Access || var == invalidBoundary { + // Ignore it, because this optimizations within scope of the --opt-access flag. + ret + } + if !isValidBoundaryInfo(maxSize) { + ret + } + for (_, mut v) in self.vars { + if v.var == var { + for i, max in v.maxSize { + if fitsSize(maxSize, max) { + // Maximum size is fits, so new size is larger than current size. + v.maxSize[i] = maxSize + ret + } + } + v.maxSize = append(v.maxSize, maxSize) + ret + } + } + // Not exist, append new one. + for (_, mut v) in self.vars { + if v.var == invalidBoundary { + // Empty place, use here instead of append. + v.var = var + v.maxSize = append(v.maxSize, maxSize) + ret + } + } + self.vars = append(self.vars, &boundaryVar{var: var, maxSize: [maxSize]}) + } + + fn removeVar(mut self, var: uintptr): bool { + if var != invalidBoundary { + for (_, mut v) in self.vars { + if v.var == var { + v.var = invalidBoundary + v.maxSize = v.maxSize[:0] + ret true + } + } + } + ret false + } + + // Reports whether maximum size of variable is fits with given expression. + fn fitsMaxSize(mut self, var: uintptr, expr: ExprModel): bool { + if var != invalidBoundary { + for _, v in self.vars { + if v.var == var { + ret fitsMaxSize(v.maxSize, expr) != -1 + } + } + } + ret false + } +} + +// Reports whether model is valid maximum size information for boundary analysis. +fn isValidBoundaryInfo(m: ExprModel): bool { ret fitsSize(m, m) } + +// Reports whether maximum size is fits with given expression. +// In other words, reports whether: max >= expr +// Returns index number of max size which is fit, otherwise -1. +fn fitsMaxSize(max: []ExprModel, expr: ExprModel): int { + for i, m in max { + if fitsSize(m, expr) { + ret i + } + } + ret -1 +} + +fn fitsSize(e1: ExprModel, e2: ExprModel): bool { + if typeData(e1) != typeData(e2) { + ret false + } + match type e1 { + | &Const: + exprConst := (&Const)(e2) + ret exprConst.AsF64() >= 0 && (&Const)(e1).GtEq(*exprConst) + | &Var: + ret e1 == e2 + | &StructSubIdentExprModel: + ssi1 := (&StructSubIdentExprModel)(e1) + ssi2 := (&StructSubIdentExprModel)(e2) + ret equalModels(ssi1.Expr.Model, ssi2.Expr.Model) && ssi1.Field == ssi2.Field + | &UnaryExprModel: + uem1 := (&UnaryExprModel)(e1) + uem2 := (&UnaryExprModel)(e2) + if uem1.Op.Id != TokenId.Star || uem1.Op.Id != uem2.Op.Id { + ret false + } + ret fitsSize(uem1.Expr.Model, uem2.Expr.Model) + |: + ret false + } +} + +fn possibleBoundaryRemove1(mut &b: &boundary, model: ExprModel) { + if b != nil { + b.removeVar(getBoundaryVar(model)) + } +} + +fn possibleBoundaryRemove2(mut &b: &boundary, model: ExprModel) { + if b == nil { + ret + } + // Clear if size data mutating. + for (_, mut v) in b.vars { + for i, max in v.maxSize { + if equalModels(max, model) { + v.maxSize = append(v.maxSize[:i], v.maxSize[i+1:]...) + break + } + } + } +} + +fn possibleBoundaryRemove(mut &b: &boundary, model: ExprModel) { + if b == nil { + ret + } + if b.removeVar(getBoundaryVar(model)) { + // Exist as a variable, so cannot be size data. + // Remove variable and return. + ret + } + // Clear if size data mutating. + for (_, mut v) in b.vars { + for i, max in v.maxSize { + if equalModels(max, model) { + v.maxSize = append(v.maxSize[:i], v.maxSize[i+1:]...) + break + } + } + } +} + +fn isBoundaryRiskyType(mut t: &TypeKind): bool { ret t.Sptr() != nil } +fn isBoundaryValidType(mut t: &TypeKind): bool { ret t.Slc() != nil || t.Arr() != nil } + +fn getBoundaryVar(m: ExprModel): uintptr { + if !Access { + ret invalidBoundary + } + match type m { + | &Var: + v := (&Var)(m) + if !v.Reference { + // Variable is not reference, return address of it. + ret uintptr((&Var)(m)) + } + // Variable is reference, it should be initialized at source code. + // Investigate the initial expression for variable address. + ret getBoundaryVar(v.Value.Data.Model) + | &StructSubIdentExprModel: + ret uintptr((&StructSubIdentExprModel)(m).Field) + | &UnaryExprModel: + uem := (&UnaryExprModel)(m) + if uem.Op.Id == TokenId.Star { // Dereferencing. + ret getBoundaryVar(uem.Expr.Model) + } + } + ret invalidBoundary +} \ No newline at end of file diff --git a/src/julec/opt/expr.jule b/src/julec/opt/expr.jule index da4b8a238..f4caf1daf 100644 --- a/src/julec/opt/expr.jule +++ b/src/julec/opt/expr.jule @@ -11,6 +11,7 @@ use std::jule::sema::{ Data, Var, FnIns, + ParamIns, ExprModel, BinaryExprModel, OperandExprModel, @@ -49,7 +50,8 @@ use types for std::jule::types // Expression optimizer that applies target-independent optimizations. struct exprOptimizer { - mut model: &ExprModel + mut model: &ExprModel + mut boundary: &boundary } impl exprOptimizer { @@ -63,6 +65,17 @@ impl exprOptimizer { } } + static fn optimizeBoundary(mut &model: ExprModel, mut &b: &boundary) { + // Do optimizatitons if any enabled. + if exprEnabled { + mut exop := &exprOptimizer{ + model: unsafe { (&ExprModel)(&model) }, + boundary: b, + } + exop.do() + } + } + fn selfCmpCond(self, mut &m: &BinaryExprModel): bool { if !equalModels(m.Left.Model, m.Right.Model) { ret false @@ -256,9 +269,60 @@ impl exprOptimizer { ret false } + fn checkBinaryForBoundary(self, mut &m: &BinaryExprModel) { + match type m.Left.Model { + | &BuiltinLenCallExprModel: + mut blc := (&BuiltinLenCallExprModel)(m.Left.Model) + if !isBoundaryValidType(blc.Expr.Kind) { + ret + } + if m.Op.Id != TokenId.Gt && m.Op.Id != TokenId.Eqs { + ret + } + // len(x) > y, len(x) == y (constant) + // Max guaranteed size of x is y. + if m.Op.Id == TokenId.Eqs { + match type m.Right.Model { + | &Const: + mut c := new(Const, *(&Const)(m.Right.Model)) // Do not mutate binary operand. + c.Sub(*Const.NewI64(1)) + self.boundary.pushVar(getBoundaryVar(blc.Expr.Model), c) + } + ret + } + self.boundary.pushVar(getBoundaryVar(blc.Expr.Model), m.Right.Model) + ret + } + match type m.Right.Model { + | &BuiltinLenCallExprModel: + mut blc := (&BuiltinLenCallExprModel)(m.Right.Model) + if !isBoundaryValidType(blc.Expr.Kind) { + ret + } + if m.Op.Id != TokenId.Lt && m.Op.Id != TokenId.Eqs { + ret + } + // y < len(x), y (constant) == len(x) + // Max guaranteed size of x is y. + if m.Op.Id == TokenId.Eqs { + match type m.Left.Model { + | &Const: + mut c := new(Const, *(&Const)(m.Left.Model)) // Do not mutate binary operand. + c.Sub(*Const.NewI64(1)) + self.boundary.pushVar(getBoundaryVar(blc.Expr.Model), c) + } + ret + } + self.boundary.pushVar(getBoundaryVar(blc.Expr.Model), m.Left.Model) + ret + } + } + fn binary(self, mut m: &BinaryExprModel) { - exprOptimizer.optimize(m.Left.Model) - exprOptimizer.optimize(m.Right.Model) + self.checkBinaryForBoundary(m) + + exprOptimizer.optimizeBoundary(m.Left.Model, self.boundary) + exprOptimizer.optimizeBoundary(m.Right.Model, self.boundary) if Cond { match { @@ -342,7 +406,7 @@ impl exprOptimizer { } fn unary(self, mut m: &UnaryExprModel) { - exprOptimizer.optimize(m.Expr.Model) + exprOptimizer.optimizeBoundary(m.Expr.Model, self.boundary) if !Ptr { ret } @@ -369,7 +433,8 @@ impl exprOptimizer { fn structureLit(self, mut m: &StructLitExprModel) { for (_, mut arg) in m.Args { - exprOptimizer.optimize(arg.Expr.Model) + possibleBoundaryRemove(self.boundary, arg.Expr.Model) + exprOptimizer.optimizeBoundary(arg.Expr.Model, self.boundary) } } @@ -378,48 +443,62 @@ impl exprOptimizer { } fn casting(self, mut m: &CastingExprModel) { - exprOptimizer.optimize(m.Expr.Model) + exprOptimizer.optimizeBoundary(m.Expr.Model, self.boundary) } - fn args(self, mut &args: []ExprModel) { + fn args(self, mut params: []&ParamIns, mut &args: []ExprModel) { for (i, mut arg) in args { - exprOptimizer.optimize(arg) + if i < len(params) { + mut p := params[i] + if p.Decl.Mutable && p.Decl.Reference { + if isBoundaryRiskyType(p.Kind) { + // Clear boundary variable if exist and sizes. + possibleBoundaryRemove(self.boundary, arg) + } else { + // Clear sizes only, no boundary variable risk. + possibleBoundaryRemove2(self.boundary, arg) + } + } + } + exprOptimizer.optimizeBoundary(arg, self.boundary) args[i] = arg } } fn scope(self, mut &s: &Scope) { mut scopt := scopeOptimizer.new(s) + scopt.boundary = self.boundary scopt.optimize() } fn funcCall(self, mut m: &FnCallExprModel) { - exprOptimizer.optimize(m.Expr) - self.args(m.Args) + exprOptimizer.optimizeBoundary(m.Expr, self.boundary) + self.args(m.Func.Params, m.Args) if m.Except != nil { self.scope(m.Except) } } fn slice(self, mut m: &SliceExprModel) { - self.args(m.Elems) + self.args(nil, m.Elems) } fn array(self, mut m: &ArrayExprModel) { if len(m.Elems) == 2 && m.Elems[1] == nil { mut elem := m.Elems[0] - exprOptimizer.optimize(elem) + exprOptimizer.optimizeBoundary(elem, self.boundary) m.Elems[0] = elem } - self.args(m.Elems) + self.args(nil, m.Elems) } fn indexing(self, mut m: &IndexingExprModel) { - exprOptimizer.optimize(m.Expr.Model) - exprOptimizer.optimize(m.Index.Model) + exprOptimizer.optimizeBoundary(m.Expr.Model, self.boundary) + exprOptimizer.optimizeBoundary(m.Index.Model, self.boundary) if !Access { ret } + array := m.Expr.Kind.Arr() != nil // Constants checked by semantic analysis for arrays, safe. if array && m.Index.IsConst() { @@ -427,21 +506,15 @@ impl exprOptimizer { *self.model = unsafe { *(*ExprModel)(&model) } ret } - match type m.Index.Model { - | &Var: - i := (&Var)(m.Index.Model) - if i.Mutable || i.IterRelation == nil { - break - } - match type m.Expr.Model { - | &Var: - r := (&Var)(m.Expr.Model) - // Iterated variable is indexed variable? - if i.IterRelation.Range == r { - mut model := any(&UnsafeIndexingExprModel{Node: m}) - *self.model = unsafe { *(*ExprModel)(&model) } - } + + if self.boundary != nil && isBoundaryValidType(m.Expr.Kind) { + var := getBoundaryVar(m.Expr.Model) + if self.boundary.fitsMaxSize(var, m.Index.Model) { + mut model := any(&UnsafeIndexingExprModel{Node: m}) + *self.model = unsafe { *(*ExprModel)(&model) } + ret } + self.boundary.pushVar(var, m.Index.Model) } } @@ -451,106 +524,106 @@ impl exprOptimizer { fn mapExpr(self, mut m: &MapExprModel) { for (_, mut pair) in m.Entries { - exprOptimizer.optimize(pair.Key) - exprOptimizer.optimize(pair.Val) + exprOptimizer.optimizeBoundary(pair.Key, self.boundary) + exprOptimizer.optimizeBoundary(pair.Val, self.boundary) } } fn slicing(self, mut m: &SlicingExprModel) { - exprOptimizer.optimize(m.Expr) - exprOptimizer.optimize(m.Left) + exprOptimizer.optimizeBoundary(m.Expr, self.boundary) + exprOptimizer.optimizeBoundary(m.Left, self.boundary) if m.Right != nil { - exprOptimizer.optimize(m.Right) + exprOptimizer.optimizeBoundary(m.Right, self.boundary) } } fn traitSub(self, mut m: &TraitSubIdentExprModel) { - exprOptimizer.optimize(m.Expr) + exprOptimizer.optimizeBoundary(m.Expr, self.boundary) } fn structureSub(self, mut m: &StructSubIdentExprModel) { - exprOptimizer.optimize(m.Expr.Model) + exprOptimizer.optimizeBoundary(m.Expr.Model, self.boundary) } fn tuple(self, mut m: &TupleExprModel) { for (_, mut d) in m.Datas { - exprOptimizer.optimize(d.Model) + exprOptimizer.optimizeBoundary(d.Model, self.boundary) } } fn newCall(self, mut m: &BuiltinNewCallExprModel) { if m.Init != nil { - exprOptimizer.optimize(m.Init) + exprOptimizer.optimizeBoundary(m.Init, self.boundary) } } fn outCall(self, mut m: &BuiltinOutCallExprModel) { - exprOptimizer.optimize(m.Expr) + exprOptimizer.optimizeBoundary(m.Expr, self.boundary) } fn outlnCall(self, mut m: &BuiltinOutlnCallExprModel) { - exprOptimizer.optimize(m.Expr) + exprOptimizer.optimizeBoundary(m.Expr, self.boundary) } fn panicCall(self, mut m: &BuiltinPanicCallExprModel) { - exprOptimizer.optimize(m.Expr) + exprOptimizer.optimizeBoundary(m.Expr, self.boundary) } fn assertCall(self, mut m: &BuiltinAssertCallExprModel) { - exprOptimizer.optimize(m.Expr) + exprOptimizer.optimizeBoundary(m.Expr, self.boundary) } fn errorCall(self, mut m: &BuiltinErrorCallExprModel) { - exprOptimizer.optimize(m.Err.Model) + exprOptimizer.optimizeBoundary(m.Err.Model, self.boundary) } fn makeCall(self, mut m: &BuiltinMakeCallExprModel) { if m.Len != nil { - exprOptimizer.optimize(m.Len) + exprOptimizer.optimizeBoundary(m.Len, self.boundary) } if m.Cap != nil { - exprOptimizer.optimize(m.Cap) + exprOptimizer.optimizeBoundary(m.Cap, self.boundary) } } fn appendCall(self, mut m: &BuiltinAppendCallExprModel) { - exprOptimizer.optimize(m.Dest) - exprOptimizer.optimize(m.Elements) + exprOptimizer.optimizeBoundary(m.Dest, self.boundary) + exprOptimizer.optimizeBoundary(m.Elements, self.boundary) } fn lenCall(self, mut m: &BuiltinLenCallExprModel) { - exprOptimizer.optimize(m.Expr.Model) + exprOptimizer.optimizeBoundary(m.Expr.Model, self.boundary) } fn capCall(self, mut m: &BuiltinCapCallExprModel) { - exprOptimizer.optimize(m.Expr.Model) + exprOptimizer.optimizeBoundary(m.Expr.Model, self.boundary) } fn deleteCall(self, mut m: &BuiltinDeleteCallExprModel) { - exprOptimizer.optimize(m.Dest.Model) + exprOptimizer.optimizeBoundary(m.Dest.Model, self.boundary) if m.Key != nil { - exprOptimizer.optimize(m.Key.Model) + exprOptimizer.optimizeBoundary(m.Key.Model, self.boundary) } } fn sizeof(self, mut m: &SizeofExprModel) { - exprOptimizer.optimize(m.Expr) + exprOptimizer.optimizeBoundary(m.Expr, self.boundary) } fn alignof(self, mut m: &AlignofExprModel) { - exprOptimizer.optimize(m.Expr) + exprOptimizer.optimizeBoundary(m.Expr, self.boundary) } fn integratedToStr(self, mut m: &IntegratedToStrExprModel) { - exprOptimizer.optimize(m.Expr) + exprOptimizer.optimizeBoundary(m.Expr, self.boundary) } fn free(self, mut m: &FreeExprModel) { - exprOptimizer.optimize(m.Expr) + exprOptimizer.optimizeBoundary(m.Expr, self.boundary) } fn backendEmit(self, mut m: &BackendEmitExprModel) { - self.args(m.Exprs) + self.args(nil, m.Exprs) } fn do(self) { diff --git a/src/julec/opt/scope.jule b/src/julec/opt/scope.jule index 76f9e5667..ab669d8c6 100644 --- a/src/julec/opt/scope.jule +++ b/src/julec/opt/scope.jule @@ -34,21 +34,25 @@ use sema for std::jule::sema::{ FallSt, TupleExprModel, BuiltinErrorCallExprModel, + Postfix, } use strings for std::strings // Scope optimizer that applies target-independent optimizations. struct scopeOptimizer { - parent: &scopeOptimizer - i: int - scope: &Scope + parent: &scopeOptimizer + i: int + scope: &Scope + boundary: &boundary } impl scopeOptimizer { static fn new(mut scope: &Scope): &scopeOptimizer { - ret &scopeOptimizer{ + mut sc := &scopeOptimizer{ scope: scope, + boundary: new(boundary), } + ret sc } fn setCurrentStmt(mut &self, mut stmt: any) { @@ -94,7 +98,7 @@ impl scopeOptimizer { ret } } - exprOptimizer.optimize(d.Model) + exprOptimizer.optimizeBoundary(d.Model, self.boundary) if Exceptional && m.Func.Decl.Exceptional && m.Except != nil && @@ -106,18 +110,18 @@ impl scopeOptimizer { } ret } - exprOptimizer.optimize(d.Model) + exprOptimizer.optimizeBoundary(d.Model, self.boundary) } fn optimizeVar(mut &self, mut v: &Var) { if v.Value != nil { - exprOptimizer.optimize(v.Value.Data.Model) + exprOptimizer.optimizeBoundary(v.Value.Data.Model, self.boundary) } } fn optimizeConditional(mut &self, mut c: &Conditional) { for (_, mut elif) in c.Elifs { - exprOptimizer.optimize(elif.Expr) + exprOptimizer.optimizeBoundary(elif.Expr, self.boundary) self.optimizeChild(elif.Scope) } if c.Default != nil { @@ -213,7 +217,7 @@ impl scopeOptimizer { fn optimizeMatch(mut &self, mut m: &Match) { for (_, mut case) in m.Cases { for (_, mut expr) in case.Exprs { - exprOptimizer.optimize(expr.Model) + exprOptimizer.optimizeBoundary(expr.Model, self.boundary) } self.optimizeChild(case.Scope) } @@ -277,7 +281,12 @@ impl scopeOptimizer { fn optimizeRangeIter(mut &self, mut it: &RangeIter) { // Optimize scope first, following alrgorithms related with expression. // It might be skip this, so scope optimizater should be guaranteed to run. + if self.boundary != nil && it.KeyA != nil { + // Add index variable to boundary analysis. + self.boundary.pushVar(getBoundaryVar(it.Expr.Model), it.KeyA) + } self.optimizeChild(it.Scope) + if Iter { match type it.Expr.Model { | &CastingExprModel: @@ -302,7 +311,7 @@ impl scopeOptimizer { | prim.IsI32(): // Expression is: []rune(str) // Avoid making allocation, iterate runes of string. - exprOptimizer.optimize(cem.Expr.Model) // Optimize string expression. + exprOptimizer.optimizeBoundary(cem.Expr.Model, self.boundary) // Optimize string expression. self.setCurrentStmt(&StrRuneIter{ Expr: cem.Expr, Base: it, @@ -311,11 +320,11 @@ impl scopeOptimizer { } } } - exprOptimizer.optimize(it.Expr.Model) + exprOptimizer.optimizeBoundary(it.Expr.Model, self.boundary) } fn optimizeWhileIter(mut &self, mut it: &WhileIter) { - exprOptimizer.optimize(it.Expr) + exprOptimizer.optimizeBoundary(it.Expr, self.boundary) self.optimizeStmt(it.Next) self.optimizeChild(it.Scope) } @@ -359,6 +368,11 @@ impl scopeOptimizer { ret self.substr(a) } + fn optimizePostfix(mut &self, mut postfix: &Postfix) { + possibleBoundaryRemove(self.boundary, postfix.Expr) + exprOptimizer.optimizeBoundary(postfix.Expr, self.boundary) + } + fn optimizeAssign(mut &self, mut assign: &sema::Assign) { if assign.Op.Id == TokenId.Eq && equalModels(assign.L.Model, assign.R.Model) { @@ -367,14 +381,16 @@ impl scopeOptimizer { ret } + possibleBoundaryRemove(self.boundary, assign.L.Model) + match { | self.strAssign(assign) | self.sliceAssign(assign): ret } - exprOptimizer.optimize(assign.L.Model) - exprOptimizer.optimize(assign.R.Model) + exprOptimizer.optimizeBoundary(assign.L.Model, self.boundary) + exprOptimizer.optimizeBoundary(assign.R.Model, self.boundary) match assign.Op.Id { | TokenId.SolidusEq | TokenId.PercentEq: @@ -398,7 +414,7 @@ impl scopeOptimizer { Left: assign.L, Right: assign.R, }) - exprOptimizer.optimize(model) + exprOptimizer.optimizeBoundary(model, self.boundary) match type model { | &BinaryExprModel: assign.R = new(OperandExprModel, *assign.R) @@ -467,8 +483,8 @@ impl scopeOptimizer { Left: assign.L[0], Right: assign.L[1], } - exprOptimizer.optimize(model.Left.Model) - exprOptimizer.optimize(model.Right.Model) + exprOptimizer.optimizeBoundary(model.Left.Model, self.boundary) + exprOptimizer.optimizeBoundary(model.Right.Model, self.boundary) self.setCurrentStmt(model) ret true } @@ -480,14 +496,15 @@ impl scopeOptimizer { for (_, mut l) in assign.L { if l != nil { - exprOptimizer.optimize(l.Model) + possibleBoundaryRemove(self.boundary, l.Model) + exprOptimizer.optimizeBoundary(l.Model, self.boundary) } } - exprOptimizer.optimize(assign.R) + exprOptimizer.optimizeBoundary(assign.R, self.boundary) } fn optimizeRet(mut &self, mut r: &RetSt) { - exprOptimizer.optimize(r.Expr) + exprOptimizer.optimizeBoundary(r.Expr, self.boundary) // Break algorithm is exceptional-specific optimizations are not enabled. // The following algorithms tries to apply specific optimizations for exceptionals. @@ -534,12 +551,15 @@ impl scopeOptimizer { self.optimizeMatch((&Match)(stmt)) | &RetSt: self.optimizeRet((&RetSt)(stmt)) + | &Postfix: + self.optimizePostfix((&Postfix)(stmt)) } } fn optimizeChild(mut &self, mut child: &Scope) { mut so := scopeOptimizer.new(child) so.parent = self + so.boundary = self.boundary so.optimize() } diff --git a/std/jule/sema/scope.jule b/std/jule/sema/scope.jule index b94128ff1..adb1a0b9b 100644 --- a/std/jule/sema/scope.jule +++ b/std/jule/sema/scope.jule @@ -1428,7 +1428,11 @@ impl scopeChecker { // Set value to nil. // Because this variable should be uninitialized declaration. // Otherwise, code generation needs more analysis. - v.Value = nil + // For references, leave values. References always should be initialized. + // So, some optimizations and utilities may use this rule. + if !v.Reference { + v.Value = nil + } st.L = append(st.L, &Data{ Lvalue: !v.Constant, diff --git a/std/jule/sema/type2.jule b/std/jule/sema/type2.jule index 27fdb3220..956a15a59 100644 --- a/std/jule/sema/type2.jule +++ b/std/jule/sema/type2.jule @@ -1108,16 +1108,8 @@ impl rangeChecker { if self.rang.KeyA == nil || IsIgnoreIdent(self.rang.KeyA.Ident) { ret } - self.Kind.KeyA = self.buildVar(self.rang.KeyA) self.Kind.KeyA.Kind = findBuiltinTypeAlias(PrimKind.Int).Kind - - match type self.d.Model { - | &Var: - self.Kind.KeyA.IterRelation = &IterRelation{ - Range: (&Var)(self.d.Model), - } - } } // Check range expression validity. diff --git a/std/jule/sema/var.jule b/std/jule/sema/var.jule index cbee1ead1..4f9eeb592 100644 --- a/std/jule/sema/var.jule +++ b/std/jule/sema/var.jule @@ -5,13 +5,6 @@ use std::jule::ast::{Directive} use std::jule::lex::{Token} -// Iteration relationship of variables. -// Stored only for indexing variable and ranged by variable. -struct IterRelation { - // Iterated variable. - Range: &Var -} - // Variable. struct Var { Scope: &Scope @@ -28,7 +21,6 @@ struct Var { Value: &Value Refers: &ReferenceStack Directives: []&Directive - IterRelation: &IterRelation // The -2 means this variable is not one of the return variables. // The -1 means this variable is just the single return variable one.