@@ -804,21 +804,52 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
804
804
match ( surface_term, expected_type. as_ref ( ) ) {
805
805
( Term :: Let ( range, def_pattern, def_type, def_expr, body_expr) , _) => {
806
806
let ( def_pattern, def_type_value) = self . synth_ann_pattern ( def_pattern, * def_type) ;
807
- let scrut = self . check_scrutinee ( def_expr, def_type_value. clone ( ) ) ;
807
+ let mut scrut = self . check_scrutinee ( def_expr, def_type_value. clone ( ) ) ;
808
808
let value = self . eval_env ( ) . eval ( scrut. expr ) ;
809
+
810
+ // Bind the scrut to a fresh variable if it is unsafe to evaluate multiple times,
811
+ // and may be evaluated multiple times by the pattern match compiler
812
+ let extra_def = match ( scrut. expr . is_trivial ( ) , def_pattern. is_trivial ( ) ) {
813
+ ( false , false ) => {
814
+ let def_name = None ; // TODO: generate a fresh name
815
+ let def_type = self . quote_env ( ) . quote ( self . scope , & scrut. r#type ) ;
816
+ let def_expr = scrut. expr . clone ( ) ;
817
+
818
+ let var = core:: Term :: LocalVar ( def_expr. span ( ) , env:: Index :: last ( ) ) ;
819
+ scrut. expr = self . scope . to_scope ( var) ;
820
+ ( self . local_env ) . push_def ( def_name, value. clone ( ) , scrut. r#type . clone ( ) ) ;
821
+ Some ( ( def_name, def_type, def_expr) )
822
+ }
823
+ _ => None ,
824
+ } ;
825
+
809
826
let initial_len = self . local_env . len ( ) ;
810
827
self . push_local_def ( & def_pattern, value, scrut. r#type . clone ( ) ) ;
811
828
let body_expr = self . check ( body_expr, & expected_type) ;
812
829
self . local_env . truncate ( initial_len) ;
813
830
814
831
let matrix = PatMatrix :: singleton ( scrut, def_pattern) ;
815
- self . elab_match (
832
+ let expr = self . elab_match (
816
833
matrix,
817
834
& [ body_expr] ,
818
835
* range,
819
836
def_expr. range ( ) ,
820
837
PatternMode :: Let ,
821
- )
838
+ ) ;
839
+ let expr = match extra_def {
840
+ None => expr,
841
+ Some ( ( def_name, def_type, def_expr) ) => {
842
+ self . local_env . pop ( ) ;
843
+ core:: Term :: Let (
844
+ range. into ( ) ,
845
+ def_name,
846
+ self . scope . to_scope ( def_type) ,
847
+ self . scope . to_scope ( def_expr) ,
848
+ self . scope . to_scope ( expr) ,
849
+ )
850
+ }
851
+ } ;
852
+ expr
822
853
}
823
854
( Term :: If ( range, cond_expr, then_expr, else_expr) , _) => {
824
855
let cond_expr = self . check ( cond_expr, & self . bool_type . clone ( ) ) ;
@@ -1110,9 +1141,25 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
1110
1141
}
1111
1142
Term :: Let ( range, def_pattern, def_type, def_expr, body_expr) => {
1112
1143
let ( def_pattern, def_type_value) = self . synth_ann_pattern ( def_pattern, * def_type) ;
1113
- let scrut = self . check_scrutinee ( def_expr, def_type_value. clone ( ) ) ;
1144
+ let mut scrut = self . check_scrutinee ( def_expr, def_type_value. clone ( ) ) ;
1114
1145
let value = self . eval_env ( ) . eval ( scrut. expr ) ;
1115
1146
1147
+ // Bind the scrut to a fresh variable if it is unsafe to evaluate multiple times,
1148
+ // and may be evaluated multiple times by the pattern match compiler
1149
+ let extra_def = match ( scrut. expr . is_trivial ( ) , def_pattern. is_trivial ( ) ) {
1150
+ ( false , false ) => {
1151
+ let def_name = None ; // TODO: generate a fresh name
1152
+ let def_type = self . quote_env ( ) . quote ( self . scope , & scrut. r#type ) ;
1153
+ let def_expr = scrut. expr . clone ( ) ;
1154
+
1155
+ let var = core:: Term :: LocalVar ( def_expr. span ( ) , env:: Index :: last ( ) ) ;
1156
+ scrut. expr = self . scope . to_scope ( var) ;
1157
+ ( self . local_env ) . push_def ( def_name, value. clone ( ) , scrut. r#type . clone ( ) ) ;
1158
+ Some ( ( def_name, def_type, def_expr) )
1159
+ }
1160
+ _ => None ,
1161
+ } ;
1162
+
1116
1163
let initial_len = self . local_env . len ( ) ;
1117
1164
self . push_local_def ( & def_pattern, value, scrut. r#type . clone ( ) ) ;
1118
1165
let ( body_expr, body_type) = self . synth ( body_expr) ;
@@ -1126,6 +1173,19 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
1126
1173
def_expr. range ( ) ,
1127
1174
PatternMode :: Let ,
1128
1175
) ;
1176
+ let expr = match extra_def {
1177
+ None => expr,
1178
+ Some ( ( def_name, def_type, def_expr) ) => {
1179
+ self . local_env . pop ( ) ;
1180
+ core:: Term :: Let (
1181
+ range. into ( ) ,
1182
+ def_name,
1183
+ self . scope . to_scope ( def_type) ,
1184
+ self . scope . to_scope ( def_expr) ,
1185
+ self . scope . to_scope ( expr) ,
1186
+ )
1187
+ }
1188
+ } ;
1129
1189
( expr, body_type)
1130
1190
}
1131
1191
Term :: If ( range, cond_expr, then_expr, else_expr) => {
@@ -1817,15 +1877,37 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
1817
1877
expected_type : & ArcValue < ' arena > ,
1818
1878
) -> core:: Term < ' arena > {
1819
1879
let expected_type = self . elim_env ( ) . force ( expected_type) ;
1820
- let scrut = self . synth_scrutinee ( scrutinee_expr) ;
1880
+ let mut scrut = self . synth_scrutinee ( scrutinee_expr) ;
1821
1881
let value = self . eval_env ( ) . eval ( scrut. expr ) ;
1822
1882
1883
+ let patterns: Vec < _ > = equations
1884
+ . iter ( )
1885
+ . map ( |( pat, _) | self . check_pattern ( pat, & scrut. r#type ) )
1886
+ . collect ( ) ;
1887
+
1888
+ // Bind the scrut to a fresh variable if it is unsafe to evaluate multiple times,
1889
+ // and may be evaluated multiple times by the pattern match compiler
1890
+ let extra_def = match (
1891
+ scrut. expr . is_trivial ( ) ,
1892
+ patterns. iter ( ) . all ( |pat| pat. is_trivial ( ) ) ,
1893
+ ) {
1894
+ ( false , false ) => {
1895
+ let def_name = None ; // TODO: generate a fresh name
1896
+ let def_type = self . quote_env ( ) . quote ( self . scope , & scrut. r#type ) ;
1897
+ let def_expr = scrut. expr . clone ( ) ;
1898
+
1899
+ let var = core:: Term :: LocalVar ( def_expr. span ( ) , env:: Index :: last ( ) ) ;
1900
+ scrut. expr = self . scope . to_scope ( var) ;
1901
+ ( self . local_env ) . push_def ( def_name, value. clone ( ) , scrut. r#type . clone ( ) ) ;
1902
+ Some ( ( def_name, def_type, def_expr) )
1903
+ }
1904
+ _ => None ,
1905
+ } ;
1906
+
1823
1907
let mut rows = Vec :: with_capacity ( equations. len ( ) ) ;
1824
1908
let mut exprs = Vec :: with_capacity ( equations. len ( ) ) ;
1825
1909
1826
- for ( pat, expr) in equations {
1827
- let pattern = self . check_pattern ( pat, & scrut. r#type ) ;
1828
-
1910
+ for ( pattern, ( _, expr) ) in patterns. into_iter ( ) . zip ( equations) {
1829
1911
let initial_len = self . local_env . len ( ) ;
1830
1912
self . push_pattern (
1831
1913
& pattern,
@@ -1841,7 +1923,21 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
1841
1923
}
1842
1924
1843
1925
let matrix = patterns:: PatMatrix :: new ( rows) ;
1844
- self . elab_match ( matrix, & exprs, range, scrut. range , PatternMode :: Match )
1926
+ let expr = self . elab_match ( matrix, & exprs, range, scrut. range , PatternMode :: Match ) ;
1927
+ let expr = match extra_def {
1928
+ None => expr,
1929
+ Some ( ( def_name, def_type, def_expr) ) => {
1930
+ self . local_env . pop ( ) ;
1931
+ core:: Term :: Let (
1932
+ range. into ( ) ,
1933
+ def_name,
1934
+ self . scope . to_scope ( def_type) ,
1935
+ self . scope . to_scope ( def_expr) ,
1936
+ self . scope . to_scope ( expr) ,
1937
+ )
1938
+ }
1939
+ } ;
1940
+ expr
1845
1941
}
1846
1942
1847
1943
fn synth_scrutinee ( & mut self , scrutinee_expr : & Term < ' _ , ByteRange > ) -> Scrutinee < ' arena > {
0 commit comments