1
//! Model-scoped representation choices for `--channelling uniform`.
2
//!
3
//! Choices concern the outer type family, ignoring domain bounds and element types. Integer
4
//! encoding is separate from matrix layout. State is scoped to a rewrite, including nested
5
//! rewrites, so enumerated models and subsequent solves never inherit another model's choices.
6
use super::{ReprInitError, ReprRulePtr};
7
use crate::ast::{DomainPtr, ReturnType, Typeable};
8
use crate::settings::{Channelling, channelling};
9
use std::cell::RefCell;
10
use std::collections::HashMap;
11
use std::mem::{Discriminant, discriminant};
12

            
13
pub(crate) type Choices = HashMap<Discriminant<ReturnType>, ReprRulePtr>;
14
thread_local! {
15
    static CHOICES: RefCell<Choices> = RefCell::new(HashMap::new());
16
}
17

            
18
247253587
fn family(dom: &DomainPtr, rule: ReprRulePtr) -> Discriminant<ReturnType> {
19
247253587
    discriminant(&if rule.is_integer_encoding() {
20
52154487
        ReturnType::Int
21
    } else {
22
195099100
        dom.return_type()
23
    })
24
247253587
}
25

            
26
/// Return the uniform representation selected for this type in the current model.
27
643672788
pub fn selected(dom: &DomainPtr, integer_encoding: bool) -> Option<ReprRulePtr> {
28
643672788
    if channelling() != Channelling::Uniform {
29
        return None;
30
643672788
    }
31
643672788
    let key = discriminant(&if integer_encoding {
32
3864693
        ReturnType::Int
33
    } else {
34
639808095
        dom.return_type()
35
    });
36
643672788
    CHOICES.with(|choices| choices.borrow().get(&key).copied())
37
643672788
}
38

            
39
243910938
pub(crate) fn check_choice(dom: &DomainPtr, rule: ReprRulePtr) -> Result<(), ReprInitError> {
40
243910938
    if channelling() == Channelling::Uniform
41
        && let Some(existing) =
42
234349574
            CHOICES.with(|choices| choices.borrow().get(&family(dom, rule)).copied())
43
104492650
        && existing.id() != rule.id()
44
    {
45
96486753
        return Err(ReprInitError::UniformConflict {
46
96486753
            domain: dom.clone(),
47
96486753
            existing: existing.name(),
48
96486753
            requested: rule.name(),
49
96486753
        });
50
147424185
    }
51
147424185
    Ok(())
52
243910938
}
53

            
54
1212007
pub(crate) fn reset() {
55
1212007
    CHOICES.with(|choices| choices.borrow_mut().clear());
56
1212007
}
57

            
58
13435775
pub(crate) fn record_choice(dom: &DomainPtr, rule: ReprRulePtr) {
59
13435775
    if channelling() == Channelling::Uniform {
60
12904013
        CHOICES.with(|choices| {
61
12904013
            choices.borrow_mut().insert(family(dom, rule), rule);
62
12904013
        });
63
531762
    }
64
13435775
}
65

            
66
/// Restore the enclosing rewrite's choices even on early return or panic.
67
pub(crate) struct Scope(Choices);
68
impl Scope {
69
1351539
    pub(crate) fn new() -> Self {
70
1351539
        Self(CHOICES.with(|choices| std::mem::take(&mut *choices.borrow_mut())))
71
1351539
    }
72
}
73
impl Drop for Scope {
74
2437311434
    fn drop(&mut self) {
75
2437311434
        CHOICES.with(|choices| *choices.borrow_mut() = std::mem::take(&mut self.0));
76
2437311434
    }
77
}
78

            
79
/// Representation probes instantiate a detached declaration. They must not select a family.
80
16777381
pub(crate) fn probe<T>(f: impl FnOnce() -> T) -> T {
81
16777381
    if channelling() != Channelling::Uniform {
82
1496534
        return f();
83
15280847
    }
84
15280847
    let _restore = Scope(CHOICES.with(|choices| choices.borrow().clone()));
85
15280847
    f()
86
16777381
}
87

            
88
/// Rule applicability is speculative. Commit choices only for the selected effect.
89
2686283788
pub(crate) fn attempt(
90
2686283788
    f: impl FnOnce() -> crate::rule_engine::ApplicationResult,
91
2686283788
) -> crate::rule_engine::ApplicationResult {
92
2686283788
    if channelling() != Channelling::Uniform {
93
313630122
        return f();
94
2372653666
    }
95
2372653666
    let previous = CHOICES.with(|choices| choices.borrow().clone());
96
2372653666
    let restore = Scope(previous.clone());
97
2372653666
    let result = f().map(|mut effect| {
98
57919204
        effect.uniform_choices = CHOICES.with(|choices| {
99
57919204
            choices
100
57919204
                .borrow()
101
57919204
                .iter()
102
123046070
                .filter(|(key, _)| !previous.contains_key(*key))
103
2027829
                .map(|(key, rule)| (*key, *rule))
104
                .collect()
105
57919204
        });
106
57919204
        effect
107
57919204
    });
108
2372653666
    drop(restore);
109
2372653666
    result
110
2686283788
}
111

            
112
64768958
pub(crate) fn commit(choices: Choices) {
113
64768958
    if channelling() == Channelling::Uniform {
114
57925352
        CHOICES.with(|current| current.borrow_mut().extend(choices));
115
6843606
    }
116
64768958
}