Files
swift-mirror/test/SILOptimizer/closure_specialization_simple.sil
Erik Eckstein 535a58e3fb SIL: assume a read-effect for unused indirect arguments
Even if an indirect argument is unused, pretend that the function reads from it.
If the unused argument is passed to another generic function and that function is specialized,
the argument may be re-abstracted and the specializer inserts a load from the indirect argument.
Therefore we must be prepared that unused indirect argument might get loaded from at some time.

Fixes a compiler crash
rdar://168623362
2026-01-23 15:58:23 +01:00

399 lines
18 KiB
Plaintext

// RUN: %target-sil-opt -enable-sil-verify-all -closure-specialization %s | %FileCheck %s
import Builtin
import Swift
sil @simple_partial_apply_fun : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
// CHECK-LABEL: sil shared [ossa] @$s27simple_partial_apply_caller0a1_b1_C4_funBi1_Tf1c_n : $@convention(thin) (Builtin.Int1) -> Builtin.Int1 {
// CHECK: bb0([[CAPTURED_ARG:%.*]] : $Builtin.Int1):
// CHECK: [[CLOSED_OVER_FUN:%.*]] = function_ref @simple_partial_apply_fun :
// CHECK: [[NEW_PAI:%.*]] = partial_apply [[CLOSED_OVER_FUN]]
// CHECK: destroy_value [[NEW_PAI]]
sil [ossa] @simple_partial_apply_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1 {
bb0(%0 : @owned $@callee_owned (Builtin.Int1) -> Builtin.Int1):
br bb1
bb1:
%1 = integer_literal $Builtin.Int1, 0
// We cannot do anything here for now but in the future I think we should try
// to handle this in closure specialization potentially.
%0c = copy_value %0
%2 = apply %0c(%1) : $@callee_owned (Builtin.Int1) -> Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
return %2 : $Builtin.Int1
}
// CHECK-LABEL: sil shared [ossa] @$s37simple_partial_apply_2nd_level_caller0a1_b1_C4_funBi1_Tf1c_n : $@convention(thin) (Builtin.Int1) -> Builtin.Int1 {
// CHECK: bb0([[CAPTURED_ARG:%.*]] : $Builtin.Int1):
// CHECK: [[SPECIALIZED_CALLEE:%.*]] = function_ref @$s27simple_partial_apply_caller0a1_b1_C4_funBi1_Tf1c_n :
// CHECK: [[RET:%.*]]= apply [[SPECIALIZED_CALLEE]]([[CAPTURED_ARG]])
// CHECK: return [[RET]]
sil [ossa] @simple_partial_apply_2nd_level_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1 {
bb0(%0 : @owned $@callee_owned (Builtin.Int1) -> Builtin.Int1):
br bb1
bb1:
%1 = function_ref @simple_partial_apply_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%0c = copy_value %0
%2 = apply %1(%0c) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
return %2 : $Builtin.Int1
}
sil @simple_partial_apply_caller_decl : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
// CHECK-LABEL: sil shared [ossa] @$s36simple_multiple_partial_apply_caller0a1_c1_D4_funBi1_ABBi1_Tf1cc_n : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1 {
// CHECK: [[C:%.*]] = function_ref @simple_partial_apply_fun : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
// CHECK: = partial_apply [[C]](%0) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
// CHECK: = partial_apply [[C]](%1) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
// CHECK: } // end sil function '$s36simple_multiple_partial_apply_caller0a1_c1_D4_funBi1_ABBi1_Tf1cc_n'
// CHECK-LABEL: sil shared [ossa] @$s36simple_multiple_partial_apply_caller19closure_with_stringSSTf1cC0_n : $@convention(thin) (@owned String) -> Builtin.Int1 {
// %0 // user: %2
// CHECK: %1 = function_ref @closure_with_string : $@convention(thin) (Builtin.Int1, @guaranteed String) -> Builtin.Int1
// CHECK: %2 = partial_apply %1(%0) : $@convention(thin) (Builtin.Int1, @guaranteed String) -> Builtin.Int1
// CHECK: %3 = copy_value %2
// CHECK: br bb1
// CHECK: } // end sil function '$s36simple_multiple_partial_apply_caller19closure_with_stringSSTf1cC0_n'
sil [ossa] @simple_multiple_partial_apply_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1, @owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1 {
bb0(%0 : @owned $@callee_owned (Builtin.Int1) -> Builtin.Int1, %1 : @owned $@callee_owned (Builtin.Int1) -> Builtin.Int1):
br bb1
bb1:
%2 = integer_literal $Builtin.Int1, 0
// We cannot do anything here for now but in the future I think we should try
// to handle this in closure specialization potentially.
%0c = copy_value %0
apply %0c(%2) : $@callee_owned (Builtin.Int1) -> Builtin.Int1
%1c = copy_value %1
apply %1c(%2) : $@callee_owned (Builtin.Int1) -> Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
destroy_value %1
return %2 : $Builtin.Int1
}
sil @simple_partial_apply_fun2 : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
sil [ossa] @simple_partial_apply_caller2 : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1 {
bb0(%0 : @owned $@callee_owned (Builtin.Int1) -> Builtin.Int1):
br bb1
bb1:
%1 = integer_literal $Builtin.Int1, 0
// We cannot do anything here for now but in the future I think we should try
// to handle this in closure specialization potentially.
%0c = copy_value %0
%2 = apply %0c(%1) : $@callee_owned (Builtin.Int1) -> Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
return %2 : $Builtin.Int1
}
sil @indirect_parameter_partial_apply_fun : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
sil [ossa] @indirect_parameter_partial_apply_caller1 : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1) -> () {
bb0(%0 : @owned $@callee_owned (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1):
br bb1
bb1:
%1 = alloc_stack $Builtin.Int1
%2 = alloc_stack $Builtin.Int1
%3 = alloc_stack $Builtin.Int1
%4 = integer_literal $Builtin.Int1, 0
store %4 to [trivial] %1
store %4 to [trivial] %2
%0c = copy_value %0
apply %0c(%3, %1, %4, %2) : $@callee_owned (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
dealloc_stack %3
dealloc_stack %2
dealloc_stack %1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @indirect_parameter_partial_apply_caller2 : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1, Builtin.Int1) -> @out Builtin.Int1) -> () {
bb0(%0 : @owned $@callee_owned (@in Builtin.Int1, Builtin.Int1) -> @out Builtin.Int1):
br bb1
bb1:
%1 = alloc_stack $Builtin.Int1
%2 = integer_literal $Builtin.Int1, 0
%0c = copy_value %0
apply %0c(%1, %1, %2) : $@callee_owned (@in Builtin.Int1, Builtin.Int1) -> @out Builtin.Int1
dealloc_stack %1 : $*Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @indirect_parameter_partial_apply_caller3 : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1) -> @out Builtin.Int1) -> () {
bb0(%0 : @owned $@callee_owned (@in Builtin.Int1) -> @out Builtin.Int1):
br bb1
bb1:
%1 = alloc_stack $Builtin.Int1
%0c = copy_value %0
apply %0c(%1, %1) : $@callee_owned (@in Builtin.Int1) -> @out Builtin.Int1
dealloc_stack %1 : $*Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @indirect_parameter_partial_apply_caller4 : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> () {
bb0(%0 : @owned $@callee_owned () -> @out Builtin.Int1):
br bb1
bb1:
%1 = alloc_stack $Builtin.Int1
%0c = copy_value %0
apply %0c(%1) : $@callee_owned () -> @out Builtin.Int1
dealloc_stack %1 : $*Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @indirect_parameter_partial_apply_caller5 : $@convention(thin) (@owned @callee_owned () -> ()) -> () {
bb0(%0 : @owned $@callee_owned () -> ()):
br bb1
bb1:
%0c = copy_value %0
apply %0c() : $@callee_owned () -> ()
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @indirect_parameter_partial_apply_caller6 : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> @out Builtin.Int1 {
bb0(%1 : $*Builtin.Int1, %0 : @owned $@callee_owned () -> @out Builtin.Int1):
br bb1
bb1:
%0c = copy_value %0
apply %0c(%1) : $@callee_owned () -> @out Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @indirect_parameter_partial_apply_caller7 : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> @out (Builtin.Int1, Builtin.Int1) {
bb0(%1 : $*(Builtin.Int1, Builtin.Int1), %0 : @owned $@callee_owned () -> @out Builtin.Int1):
br bb1
bb1:
%2 = alloc_stack $Builtin.Int1
%3 = alloc_stack $Builtin.Int1
%0c = copy_value %0
%0d = copy_value %0
apply %0c(%2) : $@callee_owned () -> @out Builtin.Int1
apply %0d(%3) : $@callee_owned () -> @out Builtin.Int1
%4 = load [trivial] %2: $*Builtin.Int1
%5 = load [trivial] %3: $*Builtin.Int1
%6 = tuple(%4 : $Builtin.Int1, %5: $Builtin.Int1)
store %6 to [trivial] %1 : $*(Builtin.Int1, Builtin.Int1)
dealloc_stack %3: $*Builtin.Int1
dealloc_stack %2: $*Builtin.Int1
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @loop_driver : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> () {
// CHECK-DAG: [[SPECIALIZED_FUN:%.*]] = function_ref @$s27simple_partial_apply_caller0a1_b1_C4_funBi1_Tf1c_n : $@convention(thin) (Builtin.Int1) -> Builtin.Int1
// CHECK: apply [[SPECIALIZED_FUN]]
// CHECK-DAG: [[SPECIALIZED_FUN2:%.*]] = function_ref @$s37simple_partial_apply_2nd_level_caller0a1_b1_C4_funBi1_Tf1c_n : $@convention(thin) (Builtin.Int1) -> Builtin.Int1
// CHECK: apply [[SPECIALIZED_FUN2]]
// We can't call this one b/c it is just a declaration.
// CHECK: [[UNSPECIALIZED_FUN_DECL:%.*]] = function_ref @simple_partial_apply_caller_decl : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
// CHECK: apply [[UNSPECIALIZED_FUN_DECL]]
// We handle closures with indirect results.
// CHECK: [[CLOSUREFUN:%.*]] = function_ref @indirect_parameter_partial_apply_fun
// CHECK-NOT: partial_apply [[CLOSUREFUN]]()
// CHECK-NOT: partial_apply [[CLOSUREFUN]]()
// We don't handle captured indirect @in and @in_guaranteed parameters yet.
// CHECK: [[CLOSURE2:%.*]] = partial_apply [[CLOSUREFUN]](%{{.*}})
// CHECK: [[CLOSURE3:%.*]] = partial_apply [[CLOSUREFUN]](%{{.*}})
// CHECK: [[CLOSURE4:%.*]] = partial_apply [[CLOSUREFUN]](%{{.*}})
// CHECK: [[CALLER1:%.*]] = function_ref @indirect_parameter_partial_apply_caller1
// CHECK: [[CALLER2:%.*]] = function_ref @indirect_parameter_partial_apply_caller2
// CHECK: [[CALLER3:%.*]] = function_ref @indirect_parameter_partial_apply_caller3
// CHECK: [[CALLER4:%.*]] = function_ref @indirect_parameter_partial_apply_caller4
// Closure with indirect result but no captured indirect parameter.
// CHECK-NOT: apply [[CALLER1]]
// CHECK: [[INLINEDCLOSURE_CALLER1:%.*]] = function_ref @$s40indirect_parameter_partial_apply_caller10a1_b1_c1_D4_funTf1c_n
// CHECK: apply [[INLINEDCLOSURE_CALLER1]]()
// CHECK-NOT: apply [[CALLER1]]
// Closures with captured indirect parameters.
// CHECK: apply [[CALLER2]]([[CLOSURE2]])
// CHECK: apply [[CALLER3]]([[CLOSURE3]])
// CHECK: apply [[CALLER4]]([[CLOSURE4]])
// CHECK: [[SM:%.*]] = function_ref @$s36simple_multiple_partial_apply_caller0a1_c1_D4_funBi1_ABBi1_Tf1cc_n : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
// CHECK: apply [[SM]](%0, %0) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
// CHECK: } // end sil function 'loop_driver'
sil [ossa] @loop_driver : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> () {
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
%2 = function_ref @simple_partial_apply_fun : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
%3 = partial_apply %2(%0) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
%4 = function_ref @simple_partial_apply_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%3c = copy_value %3
%5 = apply %4(%3c) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%51 = function_ref @simple_partial_apply_2nd_level_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%52 = apply %51(%3) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%6 = partial_apply %2(%0) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
%7 = function_ref @simple_partial_apply_caller_decl : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%8 = apply %7(%6) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%9 = alloc_stack $Builtin.Int1
%9a = alloc_stack $Builtin.Int1
%10 = function_ref @indirect_parameter_partial_apply_fun : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
%11 = partial_apply %10() : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
store %0 to [trivial] %9
%12 = partial_apply %10(%9) : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
store %0 to [trivial] %9
%13 = partial_apply %10(%1, %9) : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
store %0 to [trivial] %9
store %0 to [trivial] %9a
%14 = partial_apply %10(%9, %1, %9a) : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
%16 = function_ref @indirect_parameter_partial_apply_caller1 : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1) -> ()
%17 = function_ref @indirect_parameter_partial_apply_caller2 : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1, Builtin.Int1) -> @out Builtin.Int1) -> ()
%18 = function_ref @indirect_parameter_partial_apply_caller3 : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1) -> @out Builtin.Int1) -> ()
%19 = function_ref @indirect_parameter_partial_apply_caller4 : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> ()
%20 = function_ref @indirect_parameter_partial_apply_caller5 : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
apply %16(%11) : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1) -> ()
apply %17(%12) : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1, Builtin.Int1) -> @out Builtin.Int1) -> ()
apply %18(%13) : $@convention(thin) (@owned @callee_owned (@in Builtin.Int1) -> @out Builtin.Int1) -> ()
apply %19(%14) : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> ()
// Make sure we handle when we already have an out parameter correctly.
%21 = alloc_stack $(Builtin.Int1, Builtin.Int1)
%22 = function_ref @indirect_parameter_partial_apply_caller6 : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> @out Builtin.Int1
%23 = function_ref @indirect_parameter_partial_apply_caller7 : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> @out (Builtin.Int1, Builtin.Int1)
store %0 to [trivial] %9
store %0 to [trivial] %9a
%24 = partial_apply %10(%9, %1, %9a) : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
store %0 to [trivial] %9
store %0 to [trivial] %9a
%25 = partial_apply %10(%9, %1, %9a) : $@convention(thin) (@in Builtin.Int1, Builtin.Int1, @in Builtin.Int1) -> @out Builtin.Int1
store %0 to [trivial] %9
apply %22(%9, %24) : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> @out Builtin.Int1
apply %23(%21, %25) : $@convention(thin) (@owned @callee_owned () -> @out Builtin.Int1) -> @out (Builtin.Int1, Builtin.Int1)
dealloc_stack %21 : $*(Builtin.Int1, Builtin.Int1)
dealloc_stack %9a
dealloc_stack %9 : $*Builtin.Int1
%26 = partial_apply %2(%0) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
%27 = partial_apply %2(%0) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
%28 = function_ref @simple_multiple_partial_apply_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1, @owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%29 = apply %28(%26, %27) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1, @owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%30 = function_ref @simple_partial_apply_fun2 : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
%31 = partial_apply %30(%1) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
%32 = function_ref @simple_partial_apply_caller2 : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%33 = apply %32(%31) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%9999 = tuple()
return %9999 : $()
}
sil @closure_with_string : $@convention(thin) (Builtin.Int1, @guaranteed String) -> Builtin.Int1
// CHECK-LABEL: sil [ossa] @test_multi_closure_with_string :
// CHECK: [[C:%.*]] = copy_value %0
// CHECK: [[F:%.*]] = function_ref @$s36simple_multiple_partial_apply_caller19closure_with_stringSSTf1cC0_n : $@convention(thin) (@owned String) -> Builtin.Int1
// CHECK: = apply [[F]]([[C]]) : $@convention(thin) (@owned String) -> Builtin.Int1
// CHECK: } // end sil function 'test_multi_closure_with_string'
sil [ossa] @test_multi_closure_with_string : $@convention(thin) (@owned String) -> () {
bb0(%0 : @owned $String):
%2 = function_ref @closure_with_string : $@convention(thin) (Builtin.Int1, @guaranteed String) -> Builtin.Int1
%26 = partial_apply %2(%0) : $@convention(thin) (Builtin.Int1, @guaranteed String) -> Builtin.Int1
%27 = copy_value %26
%28 = function_ref @simple_multiple_partial_apply_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1, @owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%29 = apply %28(%26, %27) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1, @owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
%9999 = tuple()
return %9999 : $()
}