20 KiB
02 - 类型系统
Viper 的类型系统通过 t 模块提供,所有类型注解继承 t.CType 。大都以 t.C{TypeName:PascalCase}为类型,类型注解是 Viper 的核心机制,决定了变量的内存布局、大小和对齐方式。
基本类型
整数类型
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|---|---|---|---|
t.CChar |
char |
8 | 是 |
t.CShort |
short |
16 | 是 |
t.CInt |
int |
32 | 是 |
t.CLong |
long |
64 | 是 |
t.CUnsignedChar |
unsigned char |
8 | 否 |
t.CUnsignedShort |
unsigned short |
16 | 否 |
t.CUnsigned / t.CUnsignedInt |
unsigned int |
32 | 否 |
t.CUnsignedLong |
unsigned long |
64 | 否 |
t.CSignedChar |
signed char |
8 | 是 |
值得注意的是,t.CUnsigned 一般不单独使用,而是配合其它有符号类型,替换表示无符号,比如 t.CUnsigned | t.CInt,由于为了便于使用一些常用类型,则将其组合为常用的 t.CUnsignedInt 等。
固定宽度整数类型
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|---|---|---|---|
t.CInt8T |
int8_t |
8 | 是 |
t.CInt16T |
int16_t |
16 | 是 |
t.CInt32T |
int32_t |
32 | 是 |
t.CInt64T |
int64_t |
64 | 是 |
t.CUInt8T |
uint8_t |
8 | 否 |
t.CUInt16T |
uint16_t |
16 | 否 |
t.CUInt32T |
uint32_t |
32 | 否 |
t.CUInt64T |
uint64_t |
64 | 否 |
最小宽度整数类型
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|---|---|---|---|
t.CIntLeast8T |
int_least8_t |
≥8 | 是 |
t.CIntLeast16T |
int_least16_t |
≥16 | 是 |
t.CIntLeast32T |
int_least32_t |
≥32 | 是 |
t.CIntLeast64T |
int_least64_t |
≥64 | 是 |
t.CUIntLeast8T |
uint_least8_t |
≥8 | 否 |
t.CUIntLeast16T |
uint_least16_t |
≥16 | 否 |
t.CUIntLeast32T |
uint_least32_t |
≥32 | 否 |
t.CUIntLeast64T |
uint_least64_t |
≥64 | 否 |
最快最小宽度整数类型
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|---|---|---|---|
t.CIntFast8T |
int_fast8_t |
≥8 | 是 |
t.CIntFast16T |
int_fast16_t |
≥16 | 是 |
t.CIntFast32T |
int_fast32_t |
≥32 | 是 |
t.CIntFast64T |
int_fast64_t |
≥64 | 是 |
t.CUIntFast8T |
uint_fast8_t |
≥8 | 否 |
t.CUIntFast16T |
uint_fast16_t |
≥16 | 否 |
t.CUIntFast32T |
uint_fast32_t |
≥32 | 否 |
t.CUIntFast64T |
uint_fast64_t |
≥64 | 否 |
最大宽度整数类型
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|---|---|---|---|
t.CIntMaxT |
intmax_t |
≥64 | 是 |
t.CUIntMaxT |
uintmax_t |
≥64 | 否 |
stdint 短名别名
通过 import stdint 引入,这些短名是上述类型的 t.CTypedef 别名,便于在系统编程场景中快速使用。允许直接使用 from stdint import * 来直接使用。
基本类型短名
| 短名 | 等价展开 | 说明 |
|---|---|---|
stdint.INT |
t.CInt |
- |
stdint.UINT |
t.CUnsignedInt |
- |
stdint.BOOL |
t.CInt |
1 为真,0 为假 |
stdint.SHORT |
t.CShort |
- |
stdint.USHORT |
t.CUnsignedShort |
- |
stdint.LONG |
t.CLong |
- |
stdint.ULONG |
t.CUnsignedLong |
- |
stdint.LONGLONG |
t.CLong | t.CLong |
- |
stdint.ULONGLONG |
t.CUnsignedLong | t.CLong |
- |
stdint.FLOAT |
t.CFloat |
- |
stdint.DOUBLE |
t.CDouble |
- |
固定宽度短名
| 短名 | 等价展开 |
|---|---|
stdint.INT8 |
t.CInt8T |
stdint.INT16 |
t.CInt16T |
stdint.INT32 |
t.CInt32T |
stdint.INT64 |
t.CInt64T |
stdint.UINT8 |
t.CUInt8T |
stdint.UINT16 |
t.CUInt16T |
stdint.UINT32 |
t.CUInt32T |
stdint.UINT64 |
t.CUInt64T |
字节与字短名
| 短名 | 等价展开 | 说明 |
|---|---|---|
stdint.BYTE |
t.CUnsignedChar |
8 位无符号 |
stdint.WORD |
t.CUInt16T |
16 位无符号 |
stdint.DWORD |
t.CUInt32T |
32 位无符号 |
stdint.QWORD |
t.CUInt64T |
64 位无符号 |
字符类型短名
| 短名 | 等价展开 | 说明 |
|---|---|---|
stdint.TCHAR |
t.CChar |
- |
stdint.CHARLIST |
str | t.CPtr / list[str, None] |
- |
stdint.WCHAR |
stdint.WORD |
UTF-16 代码单元 |
stdint.CHAR8 |
t.CChar8T |
- |
stdint.CHAR16 |
t.CChar16T |
- |
stdint.CHAR32 |
t.CChar32T |
- |
指针短名
| 短名 | 等价展开 |
|---|---|
stdint.INTPTR |
t.CInt | t.CPtr |
stdint.UINTPTR |
t.CUnsignedInt | t.CPtr |
stdint.BYTEPTR |
stdint.BYTE | t.CPtr |
stdint.SHORTPTR |
t.CShort | t.CPtr |
stdint.USHORTPTR |
t.CUnsignedShort | t.CPtr |
stdint.WCHARPTR |
stdint.WORD | t.CPtr |
stdint.VOIDPTR |
t.CVoid | t.CPtr |
stdint.INT8PTR |
t.CInt8T | t.CPtr |
stdint.INT16PTR |
t.CInt16T | t.CPtr |
stdint.INT32PTR |
t.CInt32T | t.CPtr |
stdint.INT64PTR |
t.CInt64T | t.CPtr |
stdint.UINT8PTR |
t.CUInt8T | t.CPtr |
stdint.UINT16PTR |
t.CUInt16T | t.CPtr |
stdint.UINT32PTR |
t.CUInt32T | t.CPtr |
stdint.UINT64PTR |
t.CUInt64T | t.CPtr |
stdint.CHAR8PTR |
t.CChar8T | t.CPtr |
stdint.CHAR16PTR |
t.CChar16T | t.CPtr |
stdint.CHAR32PTR |
t.CChar32T | t.CPtr |
平台特定短名
| 短名 | 等价展开 | 说明 |
|---|---|---|
stdint.FSIZE_t |
stdint.DWORD |
文件大小 |
stdint.LBA_t |
stdint.DWORD |
逻辑块地址 |
平台相关类型
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|---|---|---|---|
t.CSizeT |
size_t |
64 | 否 |
t.CIntPtrT |
intptr_t |
64 | 是 |
t.CUIntPtrT |
uintptr_t |
64 | 否 |
t.CPtrDiffT |
ptrdiff_t |
64 | 是 |
stdint 短名别名
Viper 在 stdint 模块中提供了更简短的类型别名,通过 t.CTypedef 映射到对应的完整类型。使用时需 import stdint。
固定宽度整数短名
| 短名 | 等价 Viper 类型 | C 等价 |
|---|---|---|
INT8 |
t.CInt8T |
int8_t |
INT16 |
t.CInt16T |
int16_t |
INT32 |
t.CInt32T |
int32_t |
INT64 |
t.CInt64T |
int64_t |
UINT8 |
t.CUInt8T |
uint8_t |
UINT16 |
t.CUInt16T |
uint16_t |
UINT32 |
t.CUInt32T |
uint32_t |
UINT64 |
t.CUInt64T |
uint64_t |
固定宽度指针短名
| 短名 | 等价 Viper 类型 | C 等价 |
|---|---|---|
INT8PTR |
t.CInt8T | t.CPtr |
int8_t* |
INT16PTR |
t.CInt16T | t.CPtr |
int16_t* |
INT32PTR |
t.CInt32T | t.CPtr |
int32_t* |
INT64PTR |
t.CInt64T | t.CPtr |
int64_t* |
UINT8PTR |
t.CUInt8T | t.CPtr |
uint8_t* |
UINT16PTR |
t.CUInt16T | t.CPtr |
uint16_t* |
UINT32PTR |
t.CUInt32T | t.CPtr |
uint32_t* |
UINT64PTR |
t.CUInt64T | t.CPtr |
uint64_t* |
字符类型短名
| 短名 | 等价 Viper 类型 | C 等价 |
|---|---|---|
CHAR8 |
t.CChar8T |
char8_t |
CHAR16 |
t.CChar16T |
char16_t |
CHAR32 |
t.CChar32T |
char32_t |
CHAR8PTR |
t.CChar8T | t.CPtr |
char8_t* |
CHAR16PTR |
t.CChar16T | t.CPtr |
char16_t* |
CHAR32PTR |
t.CChar32T | t.CPtr |
char32_t* |
基本类型短名
| 短名 | 等价 Viper 类型 | C 等价 |
|---|---|---|
INT |
t.CInt |
int |
UINT |
t.CUnsignedInt |
unsigned int |
BOOL |
t.CInt |
int(0/1) |
SHORT |
t.CShort |
short |
USHORT |
t.CUnsignedShort |
unsigned short |
LONG |
t.CLong |
long |
ULONG |
t.CUnsignedLong |
unsigned long |
LONGLONG |
t.CLong | t.CLong |
long long |
ULONGLONG |
t.CUnsignedLong | t.CLong |
unsigned long long |
FLOAT |
t.CFloat |
float |
DOUBLE |
t.CDouble |
double |
平台 / Windows 风格短名
| 短名 | 等价 Viper 类型 | C 等价 |
|---|---|---|
BYTE |
t.CUnsignedChar |
unsigned char(8位) |
BYTEPTR |
BYTE | t.CPtr |
unsigned char* |
WORD |
t.CUInt16T |
uint16_t(16位) |
DWORD |
t.CUInt32T |
uint32_t(32位) |
QWORD |
t.CUInt64T |
uint64_t(64位) |
INTPTR |
t.CInt | t.CPtr |
int* |
UINTPTR |
t.CUnsignedInt | t.CPtr |
unsigned int* |
SHORTPTR |
t.CShort | t.CPtr |
short* |
USHORTPTR |
t.CUnsignedShort | t.CPtr |
unsigned short* |
VOIDPTR |
t.CVoid | t.CPtr |
void* |
TCHAR |
t.CChar |
char |
WCHAR |
WORD |
uint16_t(UTF-16) |
WCHARPTR |
WORD | t.CPtr |
uint16_t*(UTF-16) |
CHARLIST |
str | t.CPtr |
char* |
FSIZE_t |
DWORD |
uint32_t |
LBA_t |
DWORD |
uint32_t |
浮点类型
| Viper 类型 | C 等价 | 大小(位) |
|---|---|---|
t.CFloat |
float |
32 |
t.CDouble |
double |
64 |
其他基本类型
| Viper 类型 | C 等价 | 说明 |
|---|---|---|
t.CVoid |
void |
空类型 |
t.CBool |
bool |
布尔类型(8位) |
t.CWCharT |
wchar_t |
宽字符(32位) |
t.CChar8T |
char8_t |
UTF-8 字符(8位,无符号) |
t.CChar16T |
char16_t |
UTF-16 字符(16位,无符号) |
t.CChar32T |
char32_t |
UTF-32 字符(32位,无符号) |
Python 便捷映射类型
| Python 类型 | Viper 类型 | Viper 值 |
|---|---|---|
int |
t.CInt |
- |
float |
t.CFloat |
- |
str |
t.CChar | t.CPtr |
- |
bool |
t.CBool |
- |
True |
t.CBool |
1 |
False |
t.CBool |
0 |
None |
NULL |
t.CIntPtr(0) |
对于 t.py 和 c.py 的引用,必须使用 import,不能使用 from ... import ...,也不能使用 as,因为这两者都是直接以字符串识别的。
类型组合
Viper 使用 Python 的 | 来组合类型修饰符(TypeUnion),其仅在左值注解中有效,其它位置均为位或语义(至少重载前是这样的),这是 Viper 最重要的语法特性之一。
指针类型
使用 t.CPtr 表示指针,通过 | 与基本类型组合:
x: t.CInt | t.CPtr # int* — 指向 int 的指针
p: t.CVoid | t.CPtr # void* — 通用指针
s: t.CChar | t.CPtr # char* — 字符串指针
pp: t.CInt | t.CPtr | t.CPtr # int** — 指向指针的指针
等价 C 代码:
int* x;
void* p;
char* s;
int** pp;
同时,指针也可以用 Ptr[T] 的方法表示,但在 Viper 中不太推荐,虽然说这样做一直是类似工程的常见用法,但是如果此处 T 是一个多态,同时我们想要复用 Python 的高亮插件,插件无法识别 Ptr[T] 的属性取值,但是对于 T | Ptr 可以。
x: t.CPtr[t.CInt]
p: t.CPtr[t.CVoid]
s: t.CPtr[t.CChar]
pp: t.CPtr[t.CPtr[t.CInt]]
pp2: t.CInt | t.CPtr[t.CPtr] # 同时也可以这么表示
等价 C 代码:
int* x;
void* p;
char* s:
int** pp;
int** pp2;
类型限定符组合
x: t.CConst | t.CInt # const int
p: t.CConst | t.CChar | t.CPtr # const char*
存储类组合
x: t.CStatic | t.CInt # static int
f: t.CExtern | t.CInt # extern int(声明其存在于外部)
函数返回类型组合
def foo() -> t.CInt | t.CExport: # 导出函数,返回 int
return 0
def bar() -> t.CVoid | t.CExtern: # 外部函数声明
pass
t.State 声明性标记
t.State 是声明性标记类型,表示"仅声明不定义"(语义等价于 t.CExport | t.CExtern)。主要用于 FFI 外部函数声明:函数体为 pass,编译器只生成函数原型(declare),不生成定义(define),由链接器在外部库中解析符号。
def CreateFileA(lpFileName: LPCSTR, dwDesiredAccess: ULONG,
dwShareMode: ULONG,
lpSecurityAttributes: SECURITY_ATTRIBUTES | t.CPtr,
dwCreationDisposition: ULONG,
dwFlagsAndAttributes: ULONG,
hTemplateFile: HANDLE) -> HANDLE | t.State:
pass
等价 C 声明:
HANDLE CreateFileA(LPCSTR, ULONG, ULONG, SECURITY_ATTRIBUTES*, ULONG, ULONG, HANDLE);
t.State 是 Viper FFI 机制的核心,无需任何特殊语法,仅靠返回类型注解即可声明外部函数。详见 08-c-operations.md 的 FFI 章节 和 includes/w32 的 Win32 绑定。
数组类型
使用 Python 的 list[ElementType, Size] 语法表示固定大小数组(通常的,这用于栈上和 BSS 段上):
buf: list[t.CChar, 64] # char buf[64]
ids: list[t.CInt, 10] # int ids[10]
matrix: list[t.CFloat, 16] # float matrix[16]
entries: list[idt_entry, 256] # struct idt_entry entries[256]
s: list[t.CChar, None] # 自推长度 char s[];
等价 C 代码:
char buf[64];
int ids[10];
float matrix[16];
struct idt_entry entries[256];
数组指针和函数指针
lp: list[t.CInt, 12] | t.CPtr
vp: t.Callable[[t.CInt, t.CInt], t.CInt] = x # 其中 t.Callable = typing.Callable,可以直接使用。
结构体类型
使用 class 定义结构体,成员通过类型注解声明:
class point:
x: t.CInt
y: t.CInt
等价 C 代码:
struct point {
int x;
int y;
};
带指针成员的结构体
class node:
value: t.CInt
next: node | t.CPtr # struct node* next
匿名结构体成员(❌)
WARNING: 警告: 由于 Viper 在声明结构体,枚举等处不需要关键字,因此
t.Anonymouse是不需要使用的。此关键字不再维护,强行使用可能导致未定义行为。
继承 t.Anonymous 的类作为匿名成员嵌入:
class header(t.Anonymous):
magic: t.CUInt32T
version: t.CUInt32T
class packet:
header # 匿名嵌入,可直接访问 packet.magic
length: t.CUInt32T
字节序标记
WARNING: 警告: 此关键字是实验性的。未经充分测试,不当使用可能导致未定义行为。
使用 t.BigEndian 或 t.LittleEndian 标记成员的字节序:
class network_packet:
length: t.CUInt16T | t.BigEndian # 大端序存储
type: t.CUInt8T # 默认小端序
联合体类型
继承 t.CUnion 定义联合体:
class value(t.CUnion):
ival: t.CInt
fval: t.CFloat
pval: t.CVoid | t.CPtr
等价 C 代码:
union value {
int ival;
float fval;
void* pval;
};
枚举类型
继承 t.CEnum 定义枚举:
class color(t.CEnum):
RED: t.CEnum = 0
GREEN: t.CEnum = 1
BLUE: t.CEnum = 2
等价 C 代码:
enum color {
RED = 0,
GREEN = 1,
BLUE = 2
};
枚举成员可直接使用名称:
c: t.CInt = color.RED
REnum(标签联合体)
t.CEnum 是普通枚举(整数常量集合),t.REnum 是标签联合体(tagged union / Rust enum 风格)。每个变体可携带不同的 payload,通过 match 模式匹配分派。适用于少变体、无通用字段、match 分派的场景(如类型系统、IR 表示)。
class LLVMType(t.REnum):
class Int:
Bits: t.CInt
class Void:
pass
match ty:
case LLVMType.Int:
snprintf(buf, size, "i%d", ty.Bits)
case LLVMType.Void:
string.strcpy(buf, "void")
REnum 的详细用法、与 CEnum/胖节点 AST 的对比见 05-classes.md 的 REnum 章节。
Typedef
使用 t.CTypedef 创建类型别名:
irq_handler_t: t.CTypedef | t.Callable[[], t.CInt] # typedef int (*irq_handler_t)();
或通过注解赋值:
MyStruct: t.CTypedef = original_struct # typedef struct original_struct MyStruct;
更推荐通过后者的形式,前者编译器不一定能够识别到注解是对 t.CTypedef 的,还是后面的注解的,而且容易触发未定义行为。
泛型类型(PEP 695 语法)
Viper 支持 PEP 695 泛型语法 class Name[T]:,定义类型参数化的类。类型参数 T 在类体内可作为占位类型使用,实例化时用 Name[ConcreteType] 特化。
@t.NoVTable
class GSList[T]:
Head: T | t.CPtr
Tail: T | t.CPtr
Count: t.CSizeT
lst: GSList[GNode] | t.CPtr = GSList[GNode]()
泛型类还支持递归泛型继承(如 class GNode(GSListNode[GNode])),实现强类型自引用结构,无需 void* + 转型。这是 C/C++/Rust 均无法如此简洁表达的能力。
泛型类的完整用法、递归继承、实例化示例见 05-classes.md 的泛型类章节。
强制类型转换
在 Viper 中的强制类型转换有些不同,强制类型转换有两种方案,都是基于 t.CType 的用法,符合 Python 本意。
对于原先就继承自 t.CType 的类型来说,可以直接使用 call 的方法进行类型转换,比如
x: t.CChar | t.CPtr = "Hello"
t.CVoid(x, t.CPtr)
其中用法为 t.CType(x, ...),此处...可以是其它的 t.CType 或将要提到的 Typedef,将其视为组合类型对 x 进行强制转换。
额外补充一句,对于比较,四则运算,可以不使用强制类型转换,而是依靠隐式类型转换,但如果是严谨数学计算,推荐还是强制类型转换避免判断出错。
此外,若将一个不同类型的右值赋值到左值,则会隐式将右值转换为左值的类型。
如果你不喜欢 t.CVoid(x, t.CPtr) 的方法,认为这将 void 和 ptr 分开,不便于理解,那么还有一个方法,即将二者打包为一个 Typedef,即:
VOIDPTR: t.CTypedef = t.CVoid | t.CPtr
则如果你想,你可以直接通过 call 的方式进行强转,比如:
y: VOIDPTR = VOIDPTR(x)
宏定义常量
使用 t.CDefine 定义编译时常量。t.CDefine 不是数据类型,而是编译时元指令——标记为 t.CDefine 的常量在 LLVM IR 中不生成任何全局变量,而是在引用处直接内联展开。详见 01-overview.md 中 t.CDefine 深度解析。
MAX_SIZE: t.CDefine = 1024
PAGE_SIZE: t.CDefine = 4096
FA_READ: t.CDefine = 0x01
语义上等价于 C 的 #define:
#define MAX_SIZE 1024
#define PAGE_SIZE 4096
#define FA_READ 0x01
宏定义支持表达式:
CPU_FEATURE_FPU: t.CDefine = (1 << 0)
CPU_FEATURE_APIC: t.CDefine = (1 << 6)
宏定义也可与类型组合使用:
CODE_SEG: t.CDefine | t.CUInt16T = 0x08
位域
使用 t.Bit(n) 定义位域成员:
class flags:
a: t.CUInt32T | t.Bit[1] # 此处,如果存入 2,二进制是 10,会被截断为 0
b: t.CUInt32T | t.Bit[3]
c: t.CUInt32T | t.Bit[4]
LLVM IR 类型简写
Viper 提供了 LLVM IR 级别的类型简写:
i1 # 1位整数
i8 # 8位有符号整数
i16 # 16位有符号整数
i32 # 32位有符号整数
i64 # 64位有符号整数
u1 # 1位无符号整数
u8 # 8位无符号整数
u16 # 16位无符号整数
u32 # 32位无符号整数
u64 # 64位无符号整数
仍然通过 t.x 的方式调用,比如 t.i1 等。
__attribute__ 属性
通过 t.attr 命名空间访问 GCC __attribute__ 属性:
t.attr.packed() # __attribute__((packed))
t.attr.aligned(16) # __attribute__((aligned(16)))
t.attr.section(".text.startup") # __attribute__((section(".text.startup")))
t.attr.always_inline() # __attribute__((always_inline))
t.attr.noinline() # __attribute__((noinline))
t.attr.noreturn() # __attribute__((noreturn))
t.attr.weak() # __attribute__((weak))
t.attr.unused() # __attribute__((unused))
t.attr.constructor() # __attribute__((constructor))
t.attr.destructor() # __attribute__((destructor))
t.attr.deprecated("use X") # __attribute__((deprecated("use X")))
LLVM 函数属性
通过 t.attr.llvm 命名空间指定 LLVM 函数属性:
t.attr.llvm.nobuiltin
t.attr.llvm.nounwind
t.attr.llvm.noredzone
t.attr.llvm.willreturn
t.attr.llvm.mustprogress
t.attr.llvm.optnone
t.attr.llvm.noinline
t.attr.llvm.alwaysinline
t.attr.llvm.readnone
t.attr.llvm.readonly
t.attr.llvm.writeonly
t.attr.llvm.inaccessiblememonly
t.attr.llvm.inaccessiblemem_or_argmemonly
在函数返回类型注解中使用:
def foo() -> t.CInt | t.attr.llvm.nobuiltin | t.attr.llvm.nounwind:
return 0