diff --git a/Pystan/config/cfg.py b/Pystan/config/cfg.py
index 08fa8d04abcddee3e330c6267f0f3a7dc31f2b06..5f3ff2a11bbb87212395d67eec8edf7b6172dc83 100644
--- a/Pystan/config/cfg.py
+++ b/Pystan/config/cfg.py
@@ -17,6 +17,7 @@ class Label:
 @dataclass
 class LSkip(Label):
     """label skip (does nothing)"""
+    is_back_edge: bool
     def __str__(self):
         return "skip"
 
@@ -78,47 +79,41 @@ class Graph:
             self.nodes += [n2]
         n1.succs += [(n2,l)]
 
-def cfg_aux(g:Graph,e:Node,i:Node,instr:Instr) -> Node:
-    """auxiliary function to build a CfG
-    takes as argument a graph, the error node, the node starting
-    the current instruction, and the instruction itself, returns
-    the final node of current instruction
-    """
+def cfg_aux(g: "Cfg", e: Node, i: Node, instr: Instr) -> Node:
     match instr:
         case ISkip():
-           f = g.new_node()
-           g.new_edge(i,f,LSkip())
-           return f
-        case ISet(var,expr):
             f = g.new_node()
-            g.new_edge(i,f,LSet(var,expr))
-            g.new_edge(i,e,LErr(expr))
+            g.new_edge(i, f, LSkip(is_back_edge=False))
             return f
-        case ISeq(first,second):
-            m = cfg_aux(g,e,i,first)
-            return cfg_aux(g,e,m,second)
-        case ICond(cond,true_branch,false_branch):
-            t = g.new_node()
-            f = g.new_node()
-            g.new_edge(i,t,LTest(cond))
-            g.new_edge(i,f,LTest(BENeg(cond)))
-            g.new_edge(i,e,LErr(cond))
-            tf = cfg_aux(g,e,t,true_branch)
-            ff = cfg_aux(g,e,f,false_branch)
+
+        case ISet(v, expr):
             f = g.new_node()
-            g.new_edge(tf,f,LSkip())
-            g.new_edge(ff,f,LSkip())
+            g.new_edge(i, f, LSet(v, expr))
+            g.new_edge(i, e, LErr(expr))
             return f
-        case ILoop(cond,body):
+
+        case ISeq(i1, i2):
+            m = cfg_aux(g, e, i, i1)
+            return cfg_aux(g, e, m, i2)
+
+        case ICond(cond, i1, i2):
+            f = g.new_node()
+            g.new_edge(i, f, LTest(BENeg(cond)))
+            g.new_edge(i, e, LErr(cond))
+            t = cfg_aux(g, e, f, i1)
+            g.new_edge(i, f, LTest(cond))
+            return cfg_aux(g, e, t, i2)
+
+        case ILoop(cond, body):
             ib = g.new_node()
             f = g.new_node()
-            g.new_edge(i,ib,LTest(cond))
-            g.new_edge(i,f,LTest(BENeg(cond)))
-            g.new_edge(i,e,LErr(cond))
-            fb = cfg_aux(g,e,ib,body)
-            g.new_edge(fb,i,LSkip())
+            g.new_edge(i, ib, LTest(cond))          # vers boucle si cond vrai
+            g.new_edge(i, f, LTest(BENeg(cond)))    # sortie si cond faux
+            g.new_edge(i, e, LErr(cond))            # erreur si test échoue
+            fb = cfg_aux(g, e, ib, body)
+            g.new_edge(fb, i, LSkip(is_back_edge=True))  # <-- arc de retour
             return f
-        case _: assert False
+
 
 class Cfg:
     """describes a CfG"""
diff --git a/Pystan/config/iteration.py b/Pystan/config/iteration.py
index 100ea677bd4bfdcada8f6f066bfed1c9e880d55d..45a60a6daa0185612a001f473e7182068e214f3d 100644
--- a/Pystan/config/iteration.py
+++ b/Pystan/config/iteration.py
@@ -10,6 +10,11 @@ from typing import Protocol
 
 class Transfer[S](Protocol):
     """lists the functions that must be implemented for a static analysis over Lattice S"""
+
+    def widen(self, s1: S, s2: S) -> S:
+        """widening operator used on back edges (default: join)"""
+        return self.join(s1, s2)
+    
     def bottom(self) -> S:
         """the empty state, associated to unvisited nodes"""
         ...
@@ -86,7 +91,7 @@ def fixpoint_iteration[T](transfer: Transfer[T], cfg: Cfg) -> dict[Node,T]:
     output maps each node to the computed state
     """
 
-    # initialisation
+    # initialisation des états
     mapping: dict[Node, T] = {}
     for node in cfg.g.nodes:
         if node == cfg.init_node:
@@ -94,17 +99,17 @@ def fixpoint_iteration[T](transfer: Transfer[T], cfg: Cfg) -> dict[Node,T]:
         else:
             mapping[node] = transfer.bottom()
 
-    # initialisation de la worklist avec les successeurs du nœud initial
+    # initialisation de la worklist avc les successeurs du noeud initial
     worklist: list[tuple[Node, Node, Label]] = []
     for (dst, label) in cfg.init_node.succs:
         worklist.append((cfg.init_node, dst, label))
 
-    # itération principale
+    # iter principale
     while worklist:
         src, dst, label = worklist.pop(0)
         src_state = mapping[src]
 
-        # applique la fonction de transfert selon le label
+        # applique la fonction de transfert en fonction du label
         match label:
             case LSkip():
                 new_state = transfer.tr_skip(src_state)
@@ -117,13 +122,15 @@ def fixpoint_iteration[T](transfer: Transfer[T], cfg: Cfg) -> dict[Node,T]:
             case _:
                 continue  # ou raise NotImplementedError
 
-        # joindre avc l'ancien état
-        joined_state = transfer.join(mapping[dst], new_state)
+        # appliquer le widen sur un arc de retour, sinon un simple join
+        if isinstance(label, LSkip) and label.is_back_edge:
+            joined_state = transfer.widen(mapping[dst], new_state)
+        else:
+            joined_state = transfer.join(mapping[dst], new_state)
 
-        # si  nouvel état apporte de l'information, on met à jour
+        # Màj si l'état évolue
         if not transfer.included(joined_state, mapping[dst]):
             mapping[dst] = joined_state
-            # + on ajoute les successeurs à la worklist
             for (succ, lab) in dst.succs:
                 worklist.append((dst, succ, lab))