@@ -804,21 +804,52 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
804804 match ( surface_term, expected_type. as_ref ( ) ) {
805805 ( Term :: Let ( range, def_pattern, def_type, def_expr, body_expr) , _) => {
806806 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 ( ) ) ;
808808 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+
809826 let initial_len = self . local_env . len ( ) ;
810827 self . push_local_def ( & def_pattern, value, scrut. r#type . clone ( ) ) ;
811828 let body_expr = self . check ( body_expr, & expected_type) ;
812829 self . local_env . truncate ( initial_len) ;
813830
814831 let matrix = PatMatrix :: singleton ( scrut, def_pattern) ;
815- self . elab_match (
832+ let expr = self . elab_match (
816833 matrix,
817834 & [ body_expr] ,
818835 * range,
819836 def_expr. range ( ) ,
820837 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
822853 }
823854 ( Term :: If ( range, cond_expr, then_expr, else_expr) , _) => {
824855 let cond_expr = self . check ( cond_expr, & self . bool_type . clone ( ) ) ;
@@ -1110,9 +1141,25 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
11101141 }
11111142 Term :: Let ( range, def_pattern, def_type, def_expr, body_expr) => {
11121143 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 ( ) ) ;
11141145 let value = self . eval_env ( ) . eval ( scrut. expr ) ;
11151146
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+
11161163 let initial_len = self . local_env . len ( ) ;
11171164 self . push_local_def ( & def_pattern, value, scrut. r#type . clone ( ) ) ;
11181165 let ( body_expr, body_type) = self . synth ( body_expr) ;
@@ -1126,6 +1173,19 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
11261173 def_expr. range ( ) ,
11271174 PatternMode :: Let ,
11281175 ) ;
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+ } ;
11291189 ( expr, body_type)
11301190 }
11311191 Term :: If ( range, cond_expr, then_expr, else_expr) => {
@@ -1817,15 +1877,37 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
18171877 expected_type : & ArcValue < ' arena > ,
18181878 ) -> core:: Term < ' arena > {
18191879 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) ;
18211881 let value = self . eval_env ( ) . eval ( scrut. expr ) ;
18221882
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+
18231907 let mut rows = Vec :: with_capacity ( equations. len ( ) ) ;
18241908 let mut exprs = Vec :: with_capacity ( equations. len ( ) ) ;
18251909
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) {
18291911 let initial_len = self . local_env . len ( ) ;
18301912 self . push_pattern (
18311913 & pattern,
@@ -1841,7 +1923,21 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
18411923 }
18421924
18431925 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
18451941 }
18461942
18471943 fn synth_scrutinee ( & mut self , scrutinee_expr : & Term < ' _ , ByteRange > ) -> Scrutinee < ' arena > {
0 commit comments