Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
int
Rust
提交
4a4640a3
R
Rust
项目概览
int
/
Rust
接近 1 年 前同步成功
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
Rust
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
4a4640a3
编写于
7月 25, 2017
作者:
O
Oliver Schneider
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Move more non-CTFE operations to the Machine
上级
dc6e877e
变更
9
显示空白变更内容
内联
并排
Showing
9 changed file
with
282 addition
and
153 deletion
+282
-153
miri/lib.rs
miri/lib.rs
+17
-0
miri/operator.rs
miri/operator.rs
+152
-0
src/librustc_mir/interpret/const_eval.rs
src/librustc_mir/interpret/const_eval.rs
+62
-4
src/librustc_mir/interpret/error.rs
src/librustc_mir/interpret/error.rs
+15
-13
src/librustc_mir/interpret/eval_context.rs
src/librustc_mir/interpret/eval_context.rs
+3
-10
src/librustc_mir/interpret/machine.rs
src/librustc_mir/interpret/machine.rs
+21
-0
src/librustc_mir/interpret/memory.rs
src/librustc_mir/interpret/memory.rs
+2
-2
src/librustc_mir/interpret/operator.rs
src/librustc_mir/interpret/operator.rs
+6
-113
src/librustc_mir/interpret/terminator/mod.rs
src/librustc_mir/interpret/terminator/mod.rs
+4
-11
未找到文件。
miri/lib.rs
浏览文件 @
4a4640a3
...
...
@@ -7,6 +7,7 @@
#[macro_use]
extern
crate
log
;
extern
crate
log_settings
;
#[macro_use]
extern
crate
rustc
;
extern
crate
rustc_const_math
;
extern
crate
rustc_data_structures
;
...
...
@@ -25,8 +26,10 @@
pub
use
rustc_miri
::
interpret
::
*
;
mod
missing_fns
;
mod
operator
;
use
missing_fns
::
EvalContextExt
as
MissingFnsEvalContextExt
;
use
operator
::
EvalContextExt
as
OperatorEvalContextExt
;
pub
fn
eval_main
<
'a
,
'tcx
:
'a
>
(
tcx
:
TyCtxt
<
'a
,
'tcx
,
'tcx
>
,
...
...
@@ -280,4 +283,18 @@ fn call_missing_fn<'a>(
)
->
EvalResult
<
'tcx
>
{
ecx
.call_missing_fn
(
instance
,
destination
,
arg_operands
,
sig
,
path
)
}
fn
ptr_op
<
'a
>
(
ecx
:
&
rustc_miri
::
interpret
::
EvalContext
<
'a
,
'tcx
,
Self
>
,
bin_op
:
mir
::
BinOp
,
left
:
PrimVal
,
left_ty
:
ty
::
Ty
<
'tcx
>
,
right
:
PrimVal
,
right_ty
:
ty
::
Ty
<
'tcx
>
,
)
->
EvalResult
<
'tcx
,
Option
<
(
PrimVal
,
bool
)
>>
{
ecx
.ptr_op
(
bin_op
,
left
,
left_ty
,
right
,
right_ty
)
}
fn
check_non_const_fn_call
(
_
instance
:
ty
::
Instance
<
'tcx
>
)
->
EvalResult
<
'tcx
>
{
Ok
(())
}
}
miri/operator.rs
0 → 100644
浏览文件 @
4a4640a3
use
rustc
::
ty
;
use
rustc
::
mir
;
use
rustc_miri
::
interpret
::
*
;
pub
trait
EvalContextExt
<
'tcx
>
{
fn
ptr_op
(
&
self
,
bin_op
:
mir
::
BinOp
,
left
:
PrimVal
,
left_ty
:
ty
::
Ty
<
'tcx
>
,
right
:
PrimVal
,
right_ty
:
ty
::
Ty
<
'tcx
>
,
)
->
EvalResult
<
'tcx
,
Option
<
(
PrimVal
,
bool
)
>>
;
fn
ptr_int_arithmetic
(
&
self
,
bin_op
:
mir
::
BinOp
,
left
:
MemoryPointer
,
right
:
i128
,
signed
:
bool
,
)
->
EvalResult
<
'tcx
,
(
PrimVal
,
bool
)
>
;
}
impl
<
'a
,
'tcx
>
EvalContextExt
<
'tcx
>
for
EvalContext
<
'a
,
'tcx
,
super
::
Evaluator
>
{
fn
ptr_op
(
&
self
,
bin_op
:
mir
::
BinOp
,
left
:
PrimVal
,
left_ty
:
ty
::
Ty
<
'tcx
>
,
right
:
PrimVal
,
right_ty
:
ty
::
Ty
<
'tcx
>
,
)
->
EvalResult
<
'tcx
,
Option
<
(
PrimVal
,
bool
)
>>
{
use
rustc_miri
::
interpret
::
PrimValKind
::
*
;
use
rustc
::
mir
::
BinOp
::
*
;
let
usize
=
PrimValKind
::
from_uint_size
(
self
.memory
.pointer_size
());
let
isize
=
PrimValKind
::
from_int_size
(
self
.memory
.pointer_size
());
let
left_kind
=
self
.ty_to_primval_kind
(
left_ty
)
?
;
let
right_kind
=
self
.ty_to_primval_kind
(
right_ty
)
?
;
match
bin_op
{
Offset
if
left_kind
==
Ptr
&&
right_kind
==
usize
=>
{
let
pointee_ty
=
left_ty
.builtin_deref
(
true
,
ty
::
LvaluePreference
::
NoPreference
)
.expect
(
"Offset called on non-ptr type"
)
.ty
;
let
ptr
=
self
.pointer_offset
(
left
.into
(),
pointee_ty
,
right
.to_bytes
()
?
as
i64
)
?
;
Ok
(
Some
((
ptr
.into_inner_primval
(),
false
)))
},
// These work on anything
Eq
if
left_kind
==
right_kind
=>
{
let
result
=
match
(
left
,
right
)
{
(
PrimVal
::
Bytes
(
left
),
PrimVal
::
Bytes
(
right
))
=>
left
==
right
,
(
PrimVal
::
Ptr
(
left
),
PrimVal
::
Ptr
(
right
))
=>
left
==
right
,
(
PrimVal
::
Undef
,
_
)
|
(
_
,
PrimVal
::
Undef
)
=>
return
Err
(
EvalError
::
ReadUndefBytes
),
_
=>
false
,
};
Ok
(
Some
((
PrimVal
::
from_bool
(
result
),
false
)))
}
Ne
if
left_kind
==
right_kind
=>
{
let
result
=
match
(
left
,
right
)
{
(
PrimVal
::
Bytes
(
left
),
PrimVal
::
Bytes
(
right
))
=>
left
!=
right
,
(
PrimVal
::
Ptr
(
left
),
PrimVal
::
Ptr
(
right
))
=>
left
!=
right
,
(
PrimVal
::
Undef
,
_
)
|
(
_
,
PrimVal
::
Undef
)
=>
return
Err
(
EvalError
::
ReadUndefBytes
),
_
=>
true
,
};
Ok
(
Some
((
PrimVal
::
from_bool
(
result
),
false
)))
}
// These need both pointers to be in the same allocation
Lt
|
Le
|
Gt
|
Ge
|
Sub
if
left_kind
==
right_kind
&&
(
left_kind
==
Ptr
||
left_kind
==
usize
||
left_kind
==
isize
)
&&
left
.is_ptr
()
&&
right
.is_ptr
()
=>
{
let
left
=
left
.to_ptr
()
?
;
let
right
=
right
.to_ptr
()
?
;
if
left
.alloc_id
==
right
.alloc_id
{
let
res
=
match
bin_op
{
Lt
=>
left
.offset
<
right
.offset
,
Le
=>
left
.offset
<=
right
.offset
,
Gt
=>
left
.offset
>
right
.offset
,
Ge
=>
left
.offset
>=
right
.offset
,
Sub
=>
return
self
.binary_op
(
Sub
,
PrimVal
::
Bytes
(
left
.offset
as
u128
),
self
.tcx.types.usize
,
PrimVal
::
Bytes
(
right
.offset
as
u128
),
self
.tcx.types.usize
,
)
.map
(
Some
),
_
=>
bug!
(
"We already established it has to be one of these operators."
),
};
Ok
(
Some
((
PrimVal
::
from_bool
(
res
),
false
)))
}
else
{
// Both are pointers, but from different allocations.
Err
(
EvalError
::
InvalidPointerMath
)
}
}
// These work if one operand is a pointer, the other an integer
Add
|
BitAnd
|
Sub
if
left_kind
==
right_kind
&&
(
left_kind
==
usize
||
left_kind
==
isize
)
&&
left
.is_ptr
()
&&
right
.is_bytes
()
=>
{
// Cast to i128 is fine as we checked the kind to be ptr-sized
self
.ptr_int_arithmetic
(
bin_op
,
left
.to_ptr
()
?
,
right
.to_bytes
()
?
as
i128
,
left_kind
==
isize
)
.map
(
Some
)
}
Add
|
BitAnd
if
left_kind
==
right_kind
&&
(
left_kind
==
usize
||
left_kind
==
isize
)
&&
left
.is_bytes
()
&&
right
.is_ptr
()
=>
{
// This is a commutative operation, just swap the operands
self
.ptr_int_arithmetic
(
bin_op
,
right
.to_ptr
()
?
,
left
.to_bytes
()
?
as
i128
,
left_kind
==
isize
)
.map
(
Some
)
}
_
=>
Ok
(
None
)
}
}
fn
ptr_int_arithmetic
(
&
self
,
bin_op
:
mir
::
BinOp
,
left
:
MemoryPointer
,
right
:
i128
,
signed
:
bool
,
)
->
EvalResult
<
'tcx
,
(
PrimVal
,
bool
)
>
{
use
rustc
::
mir
::
BinOp
::
*
;
fn
map_to_primval
((
res
,
over
)
:
(
MemoryPointer
,
bool
))
->
(
PrimVal
,
bool
)
{
(
PrimVal
::
Ptr
(
res
),
over
)
}
Ok
(
match
bin_op
{
Sub
=>
// The only way this can overflow is by underflowing, so signdeness of the right operands does not matter
map_to_primval
(
left
.overflowing_signed_offset
(
-
right
,
self
)),
Add
if
signed
=>
map_to_primval
(
left
.overflowing_signed_offset
(
right
,
self
)),
Add
if
!
signed
=>
map_to_primval
(
left
.overflowing_offset
(
right
as
u64
,
self
)),
BitAnd
if
!
signed
=>
{
let
base_mask
:
u64
=
!
(
self
.memory
.get
(
left
.alloc_id
)
?
.align
-
1
);
let
right
=
right
as
u64
;
if
right
&
base_mask
==
base_mask
{
// Case 1: The base address bits are all preserved, i.e., right is all-1 there
(
PrimVal
::
Ptr
(
MemoryPointer
::
new
(
left
.alloc_id
,
left
.offset
&
right
)),
false
)
}
else
if
right
&
base_mask
==
0
{
// Case 2: The base address bits are all taken away, i.e., right is all-0 there
(
PrimVal
::
from_u128
((
left
.offset
&
right
)
as
u128
),
false
)
}
else
{
return
Err
(
EvalError
::
ReadPointerAsBytes
);
}
}
_
=>
{
let
msg
=
format!
(
"unimplemented binary op on pointer {:?}: {:?}, {:?} ({})"
,
bin_op
,
left
,
right
,
if
signed
{
"signed"
}
else
{
"unsigned"
});
return
Err
(
EvalError
::
Unimplemented
(
msg
));
}
})
}
}
src/librustc_mir/interpret/const_eval.rs
浏览文件 @
4a4640a3
...
...
@@ -5,7 +5,7 @@
use
syntax
::
ast
::
Mutability
;
use
super
::{
Eval
Error
,
EvalResult
,
Eval
Result
,
EvalError
,
Global
,
GlobalId
,
Lvalue
,
PrimVal
,
EvalContext
,
StackPopCleanup
,
...
...
@@ -13,6 +13,9 @@
use
rustc_const_math
::
ConstInt
;
use
std
::
fmt
;
use
std
::
error
::
Error
;
pub
fn
eval_body_as_primval
<
'a
,
'tcx
>
(
tcx
:
TyCtxt
<
'a
,
'tcx
,
'tcx
>
,
instance
:
Instance
<
'tcx
>
,
...
...
@@ -21,7 +24,7 @@ pub fn eval_body_as_primval<'a, 'tcx>(
let
mut
ecx
=
EvalContext
::
<
Evaluator
>
::
new
(
tcx
,
limits
,
(),
());
let
cid
=
GlobalId
{
instance
,
promoted
:
None
};
if
ecx
.tcx
.has_attr
(
instance
.def_id
(),
"linkage"
)
{
return
Err
(
EvalError
::
NotConst
(
"extern global"
.to_string
()
));
return
Err
(
ConstEvalError
::
NotConst
(
"extern global"
.to_string
())
.into
(
));
}
let
mir
=
ecx
.load_mir
(
instance
.def
)
?
;
...
...
@@ -75,12 +78,52 @@ pub fn eval_body_as_integer<'a, 'tcx>(
TyUint
(
UintTy
::
U64
)
=>
ConstInt
::
U64
(
prim
as
u64
),
TyUint
(
UintTy
::
U128
)
=>
ConstInt
::
U128
(
prim
),
TyUint
(
UintTy
::
Us
)
=>
ConstInt
::
Usize
(
ConstUsize
::
new
(
prim
as
u64
,
tcx
.sess.target.uint_type
)
.expect
(
"miri should already have errored"
)),
_
=>
return
Err
(
EvalError
::
NeedsRfc
(
"evaluating anything other than isize/usize during typeck"
.to_string
()
)),
_
=>
return
Err
(
ConstEvalError
::
NeedsRfc
(
"evaluating anything other than isize/usize during typeck"
.to_string
())
.into
(
)),
})
}
struct
Evaluator
;
impl
<
'tcx
>
Into
<
EvalError
<
'tcx
>>
for
ConstEvalError
{
fn
into
(
self
)
->
EvalError
<
'tcx
>
{
EvalError
::
MachineError
(
Box
::
new
(
self
))
}
}
#[derive(Clone,
Debug)]
enum
ConstEvalError
{
NeedsRfc
(
String
),
NotConst
(
String
),
}
impl
fmt
::
Display
for
ConstEvalError
{
fn
fmt
(
&
self
,
f
:
&
mut
fmt
::
Formatter
)
->
fmt
::
Result
{
use
self
::
ConstEvalError
::
*
;
match
*
self
{
NeedsRfc
(
ref
msg
)
=>
write!
(
f
,
"
\"
{}
\"
needs an rfc before being allowed inside constants"
,
msg
),
NotConst
(
ref
msg
)
=>
write!
(
f
,
"Cannot evaluate within constants:
\"
{}
\"
"
,
msg
),
}
}
}
impl
Error
for
ConstEvalError
{
fn
description
(
&
self
)
->
&
str
{
use
self
::
ConstEvalError
::
*
;
match
*
self
{
NeedsRfc
(
_
)
=>
"this feature needs an rfc before being allowed inside constants"
,
NotConst
(
_
)
=>
"this feature is not compatible with constant evaluation"
,
}
}
fn
cause
(
&
self
)
->
Option
<&
Error
>
{
None
}
}
impl
<
'tcx
>
super
::
Machine
<
'tcx
>
for
Evaluator
{
type
Data
=
();
type
MemoryData
=
();
...
...
@@ -93,6 +136,21 @@ fn call_missing_fn<'a>(
path
:
String
,
)
->
EvalResult
<
'tcx
>
{
// some simple things like `malloc` might get accepted in the future
Err
(
EvalError
::
NeedsRfc
(
format!
(
"calling extern function `{}`"
,
path
)))
Err
(
ConstEvalError
::
NeedsRfc
(
format!
(
"calling extern function `{}`"
,
path
))
.into
())
}
fn
ptr_op
<
'a
>
(
_
ecx
:
&
EvalContext
<
'a
,
'tcx
,
Self
>
,
_
bin_op
:
mir
::
BinOp
,
_
left
:
PrimVal
,
_
left_ty
:
Ty
<
'tcx
>
,
_
right
:
PrimVal
,
_
right_ty
:
Ty
<
'tcx
>
,
)
->
EvalResult
<
'tcx
,
Option
<
(
PrimVal
,
bool
)
>>
{
Err
(
ConstEvalError
::
NeedsRfc
(
"Pointer arithmetic or comparison"
.to_string
())
.into
())
}
fn
check_non_const_fn_call
(
instance
:
ty
::
Instance
<
'tcx
>
)
->
EvalResult
<
'tcx
>
{
return
Err
(
ConstEvalError
::
NotConst
(
format!
(
"calling non-const fn `{}`"
,
instance
))
.into
());
}
}
src/librustc_mir/interpret/error.rs
浏览文件 @
4a4640a3
...
...
@@ -10,8 +10,11 @@
use
rustc_const_math
::
ConstMathErr
;
use
syntax
::
codemap
::
Span
;
#[derive(
Clone,
Debug)]
#[derive(Debug)]
pub
enum
EvalError
<
'tcx
>
{
/// This variant is used by machines to signal their own errors that do not
/// match an existing variant
MachineError
(
Box
<
Error
>
),
FunctionPointerTyMismatch
(
FnSig
<
'tcx
>
,
FnSig
<
'tcx
>
),
NoMirFor
(
String
),
UnterminatedCString
(
MemoryPointer
),
...
...
@@ -95,8 +98,6 @@ pub enum EvalError<'tcx> {
HeapAllocNonPowerOfTwoAlignment
(
u64
),
Unreachable
,
Panic
,
NeedsRfc
(
String
),
NotConst
(
String
),
ReadFromReturnPointer
,
PathNotFound
(
Vec
<
String
>
),
}
...
...
@@ -107,6 +108,7 @@ impl<'tcx> Error for EvalError<'tcx> {
fn
description
(
&
self
)
->
&
str
{
use
self
::
EvalError
::
*
;
match
*
self
{
MachineError
(
ref
inner
)
=>
inner
.description
(),
FunctionPointerTyMismatch
(
..
)
=>
"tried to call a function through a function pointer of a different type"
,
InvalidMemoryAccess
=>
...
...
@@ -211,10 +213,6 @@ fn description(&self) -> &str {
"entered unreachable code"
,
Panic
=>
"the evaluated program panicked"
,
NeedsRfc
(
_
)
=>
"this feature needs an rfc before being allowed inside constants"
,
NotConst
(
_
)
=>
"this feature is not compatible with constant evaluation"
,
ReadFromReturnPointer
=>
"tried to read from the return pointer"
,
EvalError
::
PathNotFound
(
_
)
=>
...
...
@@ -222,7 +220,13 @@ fn description(&self) -> &str {
}
}
fn
cause
(
&
self
)
->
Option
<&
Error
>
{
None
}
fn
cause
(
&
self
)
->
Option
<&
Error
>
{
use
self
::
EvalError
::
*
;
match
*
self
{
MachineError
(
ref
inner
)
=>
Some
(
&**
inner
),
_
=>
None
,
}
}
}
impl
<
'tcx
>
fmt
::
Display
for
EvalError
<
'tcx
>
{
...
...
@@ -278,12 +282,10 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!
(
f
,
"expected primitive type, got {}"
,
ty
),
Layout
(
ref
err
)
=>
write!
(
f
,
"rustc layout computation failed: {:?}"
,
err
),
NeedsRfc
(
ref
msg
)
=>
write!
(
f
,
"
\"
{}
\"
needs an rfc before being allowed inside constants"
,
msg
),
NotConst
(
ref
msg
)
=>
write!
(
f
,
"Cannot evaluate within constants:
\"
{}
\"
"
,
msg
),
EvalError
::
PathNotFound
(
ref
path
)
=>
PathNotFound
(
ref
path
)
=>
write!
(
f
,
"Cannot find path {:?}"
,
path
),
MachineError
(
ref
inner
)
=>
write!
(
f
,
"machine error: {}"
,
inner
),
_
=>
write!
(
f
,
"{}"
,
self
.description
()),
}
}
...
...
src/librustc_mir/interpret/eval_context.rs
浏览文件 @
4a4640a3
...
...
@@ -666,9 +666,7 @@ pub(super) fn eval_rvalue_into_lvalue(
}
Len
(
ref
lvalue
)
=>
{
if
self
.const_env
()
{
return
Err
(
EvalError
::
NeedsRfc
(
"computing the length of arrays"
.to_string
()));
}
// FIXME(CTFE): don't allow computing the length of arrays in const eval
let
src
=
self
.eval_lvalue
(
lvalue
)
?
;
let
ty
=
self
.lvalue_ty
(
lvalue
);
let
(
_
,
len
)
=
src
.elem_ty_and_len
(
ty
);
...
...
@@ -692,9 +690,7 @@ pub(super) fn eval_rvalue_into_lvalue(
}
NullaryOp
(
mir
::
NullOp
::
Box
,
ty
)
=>
{
if
self
.const_env
()
{
return
Err
(
EvalError
::
NeedsRfc
(
"
\"
heap
\"
allocations"
.to_string
()));
}
// FIXME(CTFE): don't allow heap allocations in const eval
// FIXME: call the `exchange_malloc` lang item if available
let
size
=
self
.type_size
(
ty
)
?
.expect
(
"box only works with sized types"
);
if
size
==
0
{
...
...
@@ -708,9 +704,6 @@ pub(super) fn eval_rvalue_into_lvalue(
}
NullaryOp
(
mir
::
NullOp
::
SizeOf
,
ty
)
=>
{
if
self
.const_env
()
{
return
Err
(
EvalError
::
NeedsRfc
(
"computing the size of types (size_of)"
.to_string
()));
}
let
size
=
self
.type_size
(
ty
)
?
.expect
(
"SizeOf nullary MIR operator called for unsized type"
);
self
.write_primval
(
dest
,
PrimVal
::
from_u128
(
size
as
u128
),
dest_ty
)
?
;
}
...
...
@@ -944,7 +937,7 @@ pub(super) fn wrapping_pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>,
ptr
.wrapping_signed_offset
(
offset
,
self
)
}
pub
(
super
)
fn
pointer_offset
(
&
self
,
ptr
:
Pointer
,
pointee_ty
:
Ty
<
'tcx
>
,
offset
:
i64
)
->
EvalResult
<
'tcx
,
Pointer
>
{
pub
fn
pointer_offset
(
&
self
,
ptr
:
Pointer
,
pointee_ty
:
Ty
<
'tcx
>
,
offset
:
i64
)
->
EvalResult
<
'tcx
,
Pointer
>
{
// This function raises an error if the offset moves the pointer outside of its allocation. We consider
// ZSTs their own huge allocation that doesn't overlap with anything (and nothing moves in there because the size is 0).
// We also consider the NULL pointer its own separate allocation, and all the remaining integers pointers their own
...
...
src/librustc_mir/interpret/machine.rs
浏览文件 @
4a4640a3
...
...
@@ -6,6 +6,7 @@
EvalResult
,
EvalContext
,
Lvalue
,
PrimVal
};
use
rustc
::{
mir
,
ty
};
...
...
@@ -29,5 +30,25 @@ fn call_missing_fn<'a>(
sig
:
ty
::
FnSig
<
'tcx
>
,
path
:
String
,
)
->
EvalResult
<
'tcx
>
;
/// Called when operating on the value of pointers.
///
/// Returns `None` if the operation should be handled by the integer
/// op code
///
/// Returns a (value, overflowed) pair otherwise
fn
ptr_op
<
'a
>
(
ecx
:
&
EvalContext
<
'a
,
'tcx
,
Self
>
,
bin_op
:
mir
::
BinOp
,
left
:
PrimVal
,
left_ty
:
ty
::
Ty
<
'tcx
>
,
right
:
PrimVal
,
right_ty
:
ty
::
Ty
<
'tcx
>
,
)
->
EvalResult
<
'tcx
,
Option
<
(
PrimVal
,
bool
)
>>
;
/// Called when adding a frame for a function that's not `const fn`
///
/// Const eval returns `Err`, miri returns `Ok`
fn
check_non_const_fn_call
(
instance
:
ty
::
Instance
<
'tcx
>
)
->
EvalResult
<
'tcx
>
;
}
src/librustc_mir/interpret/memory.rs
浏览文件 @
4a4640a3
...
...
@@ -198,7 +198,7 @@ pub(crate) fn wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> S
MemoryPointer
::
new
(
self
.alloc_id
,
cx
.data_layout
()
.wrapping_signed_offset
(
self
.offset
,
i
))
}
pub
(
crate
)
fn
overflowing_signed_offset
<
C
:
HasDataLayout
>
(
self
,
i
:
i128
,
cx
:
C
)
->
(
Self
,
bool
)
{
pub
fn
overflowing_signed_offset
<
C
:
HasDataLayout
>
(
self
,
i
:
i128
,
cx
:
C
)
->
(
Self
,
bool
)
{
let
(
res
,
over
)
=
cx
.data_layout
()
.overflowing_signed_offset
(
self
.offset
,
i
);
(
MemoryPointer
::
new
(
self
.alloc_id
,
res
),
over
)
}
...
...
@@ -207,7 +207,7 @@ pub(crate) fn signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult
Ok
(
MemoryPointer
::
new
(
self
.alloc_id
,
cx
.data_layout
()
.signed_offset
(
self
.offset
,
i
)
?
))
}
pub
(
crate
)
fn
overflowing_offset
<
C
:
HasDataLayout
>
(
self
,
i
:
u64
,
cx
:
C
)
->
(
Self
,
bool
)
{
pub
fn
overflowing_offset
<
C
:
HasDataLayout
>
(
self
,
i
:
u64
,
cx
:
C
)
->
(
Self
,
bool
)
{
let
(
res
,
over
)
=
cx
.data_layout
()
.overflowing_offset
(
self
.offset
,
i
);
(
MemoryPointer
::
new
(
self
.alloc_id
,
res
),
over
)
}
...
...
src/librustc_mir/interpret/operator.rs
浏览文件 @
4a4640a3
use
rustc
::
mir
;
use
rustc
::
ty
::
{
self
,
Ty
}
;
use
rustc
::
ty
::
Ty
;
use
super
::{
EvalError
,
EvalResult
,
EvalContext
,
MemoryPointer
,
Lvalue
,
Machine
,
};
...
...
@@ -153,75 +152,9 @@ pub fn binary_op(
//trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
// I: Handle operations that support pointers
let
usize
=
PrimValKind
::
from_uint_size
(
self
.memory
.pointer_size
());
let
isize
=
PrimValKind
::
from_int_size
(
self
.memory
.pointer_size
());
if
!
left_kind
.is_float
()
&&
!
right_kind
.is_float
()
{
if
(
!
left
.is_bytes
()
&&
!
right
.is_bytes
())
&&
self
.const_env
()
{
return
Err
(
EvalError
::
NeedsRfc
(
"Pointer arithmetic or comparison"
.to_string
()));
}
match
bin_op
{
Offset
if
left_kind
==
Ptr
&&
right_kind
==
usize
=>
{
let
pointee_ty
=
left_ty
.builtin_deref
(
true
,
ty
::
LvaluePreference
::
NoPreference
)
.expect
(
"Offset called on non-ptr type"
)
.ty
;
let
ptr
=
self
.pointer_offset
(
left
.into
(),
pointee_ty
,
right
.to_bytes
()
?
as
i64
)
?
;
return
Ok
((
ptr
.into_inner_primval
(),
false
));
},
// These work on anything
Eq
if
left_kind
==
right_kind
=>
{
let
result
=
match
(
left
,
right
)
{
(
PrimVal
::
Bytes
(
left
),
PrimVal
::
Bytes
(
right
))
=>
left
==
right
,
(
PrimVal
::
Ptr
(
left
),
PrimVal
::
Ptr
(
right
))
=>
left
==
right
,
(
PrimVal
::
Undef
,
_
)
|
(
_
,
PrimVal
::
Undef
)
=>
return
Err
(
EvalError
::
ReadUndefBytes
),
_
=>
false
,
};
return
Ok
((
PrimVal
::
from_bool
(
result
),
false
));
}
Ne
if
left_kind
==
right_kind
=>
{
let
result
=
match
(
left
,
right
)
{
(
PrimVal
::
Bytes
(
left
),
PrimVal
::
Bytes
(
right
))
=>
left
!=
right
,
(
PrimVal
::
Ptr
(
left
),
PrimVal
::
Ptr
(
right
))
=>
left
!=
right
,
(
PrimVal
::
Undef
,
_
)
|
(
_
,
PrimVal
::
Undef
)
=>
return
Err
(
EvalError
::
ReadUndefBytes
),
_
=>
true
,
};
return
Ok
((
PrimVal
::
from_bool
(
result
),
false
));
}
// These need both pointers to be in the same allocation
Lt
|
Le
|
Gt
|
Ge
|
Sub
if
left_kind
==
right_kind
&&
(
left_kind
==
Ptr
||
left_kind
==
usize
||
left_kind
==
isize
)
&&
left
.is_ptr
()
&&
right
.is_ptr
()
=>
{
let
left
=
left
.to_ptr
()
?
;
let
right
=
right
.to_ptr
()
?
;
if
left
.alloc_id
==
right
.alloc_id
{
let
res
=
match
bin_op
{
Lt
=>
left
.offset
<
right
.offset
,
Le
=>
left
.offset
<=
right
.offset
,
Gt
=>
left
.offset
>
right
.offset
,
Ge
=>
left
.offset
>=
right
.offset
,
Sub
=>
{
return
int_arithmetic!
(
left_kind
,
overflowing_sub
,
left
.offset
,
right
.offset
);
}
_
=>
bug!
(
"We already established it has to be one of these operators."
),
};
return
Ok
((
PrimVal
::
from_bool
(
res
),
false
));
}
else
{
// Both are pointers, but from different allocations.
return
Err
(
EvalError
::
InvalidPointerMath
);
}
}
// These work if one operand is a pointer, the other an integer
Add
|
BitAnd
|
Sub
if
left_kind
==
right_kind
&&
(
left_kind
==
usize
||
left_kind
==
isize
)
&&
left
.is_ptr
()
&&
right
.is_bytes
()
=>
{
// Cast to i128 is fine as we checked the kind to be ptr-sized
return
self
.ptr_int_arithmetic
(
bin_op
,
left
.to_ptr
()
?
,
right
.to_bytes
()
?
as
i128
,
left_kind
==
isize
);
}
Add
|
BitAnd
if
left_kind
==
right_kind
&&
(
left_kind
==
usize
||
left_kind
==
isize
)
&&
left
.is_bytes
()
&&
right
.is_ptr
()
=>
{
// This is a commutative operation, just swap the operands
return
self
.ptr_int_arithmetic
(
bin_op
,
right
.to_ptr
()
?
,
left
.to_bytes
()
?
as
i128
,
left_kind
==
isize
);
}
_
=>
{}
if
let
Some
(
handled
)
=
M
::
ptr_op
(
self
,
bin_op
,
left
,
left_ty
,
right
,
right_ty
)
?
{
return
Ok
(
handled
);
}
}
...
...
@@ -270,6 +203,9 @@ pub fn binary_op(
(
Div
,
F64
)
=>
f64_arithmetic!
(
/
,
l
,
r
),
(
Rem
,
F64
)
=>
f64_arithmetic!
(
%
,
l
,
r
),
(
Eq
,
_
)
=>
PrimVal
::
from_bool
(
l
==
r
),
(
Ne
,
_
)
=>
PrimVal
::
from_bool
(
l
!=
r
),
(
Lt
,
k
)
if
k
.is_signed_int
()
=>
PrimVal
::
from_bool
((
l
as
i128
)
<
(
r
as
i128
)),
(
Lt
,
_
)
=>
PrimVal
::
from_bool
(
l
<
r
),
(
Le
,
k
)
if
k
.is_signed_int
()
=>
PrimVal
::
from_bool
((
l
as
i128
)
<=
(
r
as
i128
)),
...
...
@@ -297,49 +233,6 @@ pub fn binary_op(
Ok
((
val
,
false
))
}
fn
ptr_int_arithmetic
(
&
self
,
bin_op
:
mir
::
BinOp
,
left
:
MemoryPointer
,
right
:
i128
,
signed
:
bool
,
)
->
EvalResult
<
'tcx
,
(
PrimVal
,
bool
)
>
{
use
rustc
::
mir
::
BinOp
::
*
;
fn
map_to_primval
((
res
,
over
)
:
(
MemoryPointer
,
bool
))
->
(
PrimVal
,
bool
)
{
(
PrimVal
::
Ptr
(
res
),
over
)
}
Ok
(
match
bin_op
{
Sub
=>
// The only way this can overflow is by underflowing, so signdeness of the right operands does not matter
map_to_primval
(
left
.overflowing_signed_offset
(
-
right
,
self
)),
Add
if
signed
=>
map_to_primval
(
left
.overflowing_signed_offset
(
right
,
self
)),
Add
if
!
signed
=>
map_to_primval
(
left
.overflowing_offset
(
right
as
u64
,
self
)),
BitAnd
if
!
signed
=>
{
let
base_mask
:
u64
=
!
(
self
.memory
.get
(
left
.alloc_id
)
?
.align
-
1
);
let
right
=
right
as
u64
;
if
right
&
base_mask
==
base_mask
{
// Case 1: The base address bits are all preserved, i.e., right is all-1 there
(
PrimVal
::
Ptr
(
MemoryPointer
::
new
(
left
.alloc_id
,
left
.offset
&
right
)),
false
)
}
else
if
right
&
base_mask
==
0
{
// Case 2: The base address bits are all taken away, i.e., right is all-0 there
(
PrimVal
::
from_u128
((
left
.offset
&
right
)
as
u128
),
false
)
}
else
{
return
Err
(
EvalError
::
ReadPointerAsBytes
);
}
}
_
=>
{
let
msg
=
format!
(
"unimplemented binary op on pointer {:?}: {:?}, {:?} ({})"
,
bin_op
,
left
,
right
,
if
signed
{
"signed"
}
else
{
"unsigned"
});
return
Err
(
EvalError
::
Unimplemented
(
msg
));
}
})
}
}
pub
fn
unary_op
<
'tcx
>
(
...
...
src/librustc_mir/interpret/terminator/mod.rs
浏览文件 @
4a4640a3
...
...
@@ -40,9 +40,7 @@ pub(super) fn eval_terminator(
Goto
{
target
}
=>
self
.goto_block
(
target
),
SwitchInt
{
ref
discr
,
ref
values
,
ref
targets
,
..
}
=>
{
if
self
.const_env
()
{
return
Err
(
EvalError
::
NeedsRfc
(
"branching (if, match, loop, ...)"
.to_string
()));
}
// FIXME(CTFE): forbid branching
let
discr_val
=
self
.eval_operand
(
discr
)
?
;
let
discr_ty
=
self
.operand_ty
(
discr
);
let
discr_prim
=
self
.value_to_primval
(
discr_val
,
discr_ty
)
?
;
...
...
@@ -100,9 +98,7 @@ pub(super) fn eval_terminator(
Drop
{
ref
location
,
target
,
..
}
=>
{
trace!
(
"TerminatorKind::drop: {:?}, {:?}"
,
location
,
self
.substs
());
if
self
.const_env
()
{
return
Err
(
EvalError
::
NeedsRfc
(
"invoking `Drop::drop`"
.to_string
()));
}
// FIXME(CTFE): forbid drop in const eval
let
lval
=
self
.eval_lvalue
(
location
)
?
;
let
ty
=
self
.lvalue_ty
(
location
);
self
.goto_block
(
target
);
...
...
@@ -436,17 +432,14 @@ fn eval_fn_call_inner(
let
mir
=
match
self
.load_mir
(
instance
.def
)
{
Ok
(
mir
)
=>
mir
,
Err
(
EvalError
::
NoMirFor
(
path
))
=>
{
if
self
.const_env
()
{
return
Err
(
EvalError
::
NeedsRfc
(
format!
(
"calling extern function `{}`"
,
path
)));
}
M
::
call_missing_fn
(
self
,
instance
,
destination
,
arg_operands
,
sig
,
path
)
?
;
return
Ok
(
true
);
},
Err
(
other
)
=>
return
Err
(
other
),
};
if
self
.const_env
()
&&
!
self
.tcx
.is_const_fn
(
instance
.def_id
())
{
return
Err
(
EvalError
::
NotConst
(
format!
(
"calling non-const fn `{}`"
,
instance
)))
;
if
!
self
.tcx
.is_const_fn
(
instance
.def_id
())
{
M
::
check_non_const_fn_call
(
instance
)
?
;
}
let
(
return_lvalue
,
return_to_block
)
=
match
destination
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录