fn canonicalize()

in checker/src/path.rs [833:1006]


    fn canonicalize(&self, environment: &Environment) -> Rc<Path> {
        if let Some(val) = environment.value_at(self) {
            // If self binds to value &p then self and path &p are equivalent paths.
            // Since many paths can bind to &p and p is canonical (by construction of &p).
            // we replace self with &p.

            // This reasoning is extended to any expression that has type ExpressionType::ThinPointer.
            // With one exception: we guard against the case where self may bind to old(self).
            // This can happen when self is rooted in a parameter and has been accessed before
            // being assigned to. After any assignment, self and old(self) are not aliases so
            // they need to stay distinct, not least because otherwise the first assignment
            // to self will actually update old(self).
            let mut not_dummy = true;
            if let Expression::InitialParameterValue { path, .. } = &val.expression {
                if self.eq(path) {
                    not_dummy = false;
                }
            }
            // If self binds to value &p then self and path &p are equivalent paths.
            // Since self is derived from p, we use path &p as the canonical form.
            // If we used self instead, then what would we do if we encounter another
            // path that also binds to value &p?
            if not_dummy && val.expression.infer_type() == ExpressionType::ThinPointer {
                if matches!(&val.expression, Expression::Offset { .. }) {
                    return Path::get_as_path(val.clone());
                }
                return Path::new_computed(val.clone());
            }
            // If the environment contains a value for (key) self, self must be canonical.
            // At any rate, it makes no sense to turn it into another path that likely
            // does not lead to the known value.
            return self.clone();
        }

        // If self is a qualified path, then recursive canonicalization of the qualifier may
        // cause substitutions (as above) and that could result in a non canonical qualified path
        // if *p becomes *&q. We thus need some additional logic to canonicalize the overall
        // path once the qualifier has been canonicalized.
        if let PathEnum::QualifiedPath {
            qualifier,
            selector,
            ..
        } = &self.value
        {
            let canonical_qualifier = qualifier.canonicalize(environment);
            // If the canonical qualifier binds to a special value, we may need to deconstruct that
            // value before constructing the qualified path.
            let qualifier_as_value = if let PathEnum::Computed { value }
            | PathEnum::Offset { value } = &canonical_qualifier.value
            {
                Some(value)
            } else {
                environment.value_at(&canonical_qualifier)
            };

            if let Some(value) = qualifier_as_value {
                match &value.expression {
                    Expression::InitialParameterValue { path, .. } => {
                        let ps = Path::new_qualified(path.clone(), selector.clone());
                        return if let Some(v) = environment.value_at(&ps) {
                            if let Expression::InitialParameterValue { path: p, .. } = &v.expression
                            {
                                if ps.eq(p) {
                                    // The p.s is the key for old(p.s), so ps by itself is unambiguous.
                                    return ps;
                                }
                            }
                            if matches!(path.value, PathEnum::Parameter { .. }) {
                                // old(p).s => p.s if there has been no assignment to p, which is
                                // the case because qualifier_as_value is a dummy value.
                                Path::new_qualified(path.clone(), selector.clone())
                            } else {
                                Path::new_qualified(
                                    Path::new_computed(value.clone()),
                                    selector.clone(),
                                )
                            }
                        } else {
                            // Since there is no entry for ps in the environment, looking up
                            // its value will create it (in a context where the type of p.s
                            // is known).
                            ps
                        };
                    }
                    Expression::Offset { left, right } if right.is_zero() => {
                        if let Expression::Reference(p) = &left.expression {
                            // *offset(&p, 0) becomes p
                            if **selector == PathSelector::Deref {
                                return p.clone();
                            }
                        }
                        // offset(p, 0) becomes p in a qualifier
                        let p = Path::get_as_path(value.clone());
                        return Path::new_qualified(p, selector.clone());
                    }
                    // *&p is equivalent to p and (&p).q is equivalent to p.q, etc.
                    Expression::Reference(p) => {
                        // *&p just becomes p
                        // (except when the value at p is structured and the result is assigned to a local,
                        // but such paths are never canonicalized).
                        if **selector == PathSelector::Deref {
                            return p.clone();
                        }
                        // since self is a qualified path we have to drop the reference operator
                        // since selectors implicitly dereference pointers.
                        return Path::new_qualified(p.clone(), selector.clone());
                    }
                    Expression::Variable { path, .. } => {
                        return Path::new_qualified(path.clone(), selector.clone());
                    }
                    _ => {
                        if **selector == PathSelector::Deref {
                            return Path::new_qualified(
                                Path::get_as_path(value.clone()),
                                selector.clone(),
                            );
                        }
                    }
                }
            }
            // An impossible downcast is equivalent to BOTTOM
            if let PathSelector::Downcast(_, variant) = selector.as_ref() {
                let discriminator = Path::new_discriminant(canonical_qualifier.clone());
                if let Some(val) = environment.value_at(&discriminator) {
                    if let Expression::CompileTimeConstant(ConstantDomain::U128(ordinal)) =
                        &val.expression
                    {
                        if (*variant as u128) != *ordinal {
                            // The downcast is impossible in this calling context
                            return Path::new_computed(Rc::new(abstract_value::BOTTOM));
                        }
                    }
                }
            }
            if let PathSelector::Slice(count) = selector.as_ref() {
                if let Expression::CompileTimeConstant(ConstantDomain::U128(count)) =
                    &count.expression
                {
                    if let PathEnum::QualifiedPath {
                        qualifier,
                        selector,
                        ..
                    } = &qualifier.value
                    {
                        if **selector == PathSelector::Deref {
                            if let PathEnum::Offset { value } = &qualifier.value {
                                if let Expression::Offset { left, right } = &value.expression {
                                    if let Expression::CompileTimeConstant(ConstantDomain::I128(
                                        from,
                                    )) = &right.expression
                                    {
                                        let base_path = Path::new_deref(
                                            Path::get_as_path(left.clone()),
                                            ExpressionType::NonPrimitive,
                                        );
                                        let to = *from + (*count as i128);
                                        let selector = Rc::new(PathSelector::ConstantSlice {
                                            from: *from as u64,
                                            to: to as u64,
                                            from_end: false,
                                        });
                                        return Path::new_qualified(base_path, selector);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            Path::new_qualified(canonical_qualifier, selector.clone())
        } else {
            self.clone()
        }
    }