网教网

搜索
查看: 142|回复: 12

设计一种新的系统编程语言

[复制链接]

1

主题

1

帖子

3

积分

新手上路

Rank: 1

积分
3
发表于 2022-9-21 14:17:02 | 显示全部楼层 |阅读模式
Go语言的有些方面其实设计的挺好的,能够细粒度的控制内存布局,又有垃圾收集器来避免内存问题。是易用性和性能的很好折中。但是Go的语法很怪异,在"最丑编程语言"的回答里面都被多次提名。而且缺少继承多态、异常处理、泛型(最近可能会有)等关键特性。如果能有一种编程语言,语法和C一致,有指针和GC,能灵活的控制内存布局,能解决内存安全问题,那我一定会用的。可惜没有见到这样的语言,我只能自己来设计了。
指针安全性

有指针以后,第一个安全问题就是野指针问题,也就是长生命期的引用了短生命期。我的解决方案是将指针生命期分为三类。生命期: 堆内存/全局变量>参数/返回值>局部变量。编译器检查规则是:长生命期可以引用短生命期,短生命期不能引用长生命期。
参数和本地生命期情况:
int* foo4(int *b) {
  int b2;
  return &b2; //compile error: 本地生命期 < 参数/返回值生命期
  return b; //ok
}指针有一个heap关键字来表示是在堆上分配的。heap关键字有三个作用:1.表明指针是堆上分配的,2.在堆上分配内存,3.将栈上对象boxing到堆上。鉴于heap关键字这么常用,那么这个编程语言就叫heap语言。
struct C {
  int b;
}

int* g;
void foo1(heap int* b) {
  g = b;
}
void foo2(int* b) {
  g = heap b;
}
void foo3(int b) {
  g = heap b;
}

x := C();
foo1(&x.b) //compile error
foo1(head x.b)
foo2(&x.b)
foo3(x.b)

y := heap C();
foo1(&y.b)
foo2(&y.b)
foo3(y.b)结构体内部成员的生命期取决于父级的生命期。
两个未标明参数的相互引用比较棘手,暂时先当错误处理。例如:
struct A {
  int *b;
}
void foo(A* a, int* b) {
  a.b = b; //compile error
}
void foo(A* a, heap int* b) {
  a.b = b; //ok
}返回值生命期提升问题。感谢Nugine提出这个问题,我之前没有想到。
A foo() {
  int i;
  A a;
  a.b = &i;
  return a; //compile error
}这种情况需要对返回值进行严格检查,返回的结构体中不能包含非heap指针。
继承与多态

我觉得继承和多态是语言抽象能力的重要体现。并且这种抽象没有性能开销。如果信了组合优于继承的话,可以不用这个特性,不是强制的。
和Java一样的单类多接口继承。只有标记为virtual的结构才能有虚方法。
interface I {
  virtual void foo() { ... }
}

virtual struct B {
  int a;
  virtual void bar() { ... }
}

struct A : B, I {
  B* b;
  override void foo(B* b) {
    ...
  }
  override void bar() {...}
}因为有GC,所以不需要RAII,不需要构造函数和析构函数。
指针使用

指针使用点语法调用方法,而不用->
A a;
A* b;
a.foo();
b.foo();指针类型转换。
A *a = dynamic_cast<A*>(p);
B *a = unsafe_cast<B*>(p);指针默认不允许运算,可临时转为数组处理。
数组

数组定义和C一致,但是传参数时变成一种自带长度的胖指针。数组在运行时检查越界。
int[14] a;
foo(int[] a) {
  print(a.length);
  print(a[0]);
}
foo(a);数组和指针相互转换
int *p;
int[] a = as_array(p, 5);

int *q = a.pointer;异常

众所周知C++中没人使用异常。并不是异常不好,而是异常和手动内存管理不兼容。既然选择了GC,那么异常和GC很搭配。所有可能抛出异常的函数必须使用throws关键字标记。
void foo() throws {
}

void dar() throws {
  foo();
}

void bar2() {
  try {
    foo();
  }
  catch (Exception *e) {
  }
}模板泛型

使用了C++类似的模板来支持泛型。
struct Bar<T> {
  void foo(): T {
    ...
  }
}null

指针默认不能为null,除非后面有问号
B* a;
B*? a = null;垃圾收集

为什么一定要有GC。手动管理很容易造成内存安全问题。有GC也避免了RAII和析构函数这些机制。GC的开销其实也没有很大,在可接受的范围。
getter/setter

使用操作符重载的方式来定义getter/setter。
struct Bar {
  private int _size;
  setter void size(int s) {
    this._size = s;
  }
}

Bar b;
b.size = 2; // call b.size(2);去掉的特性

并不是特性越多越好,去掉很多C++的特性。

  • 不支持引用,只有指针。
  • 没有class,只有struct。
  • 没有头文件
  • 不支持函数通过参数重载
  • 不支持一次定义多个变量。
  • 不支持RAII
  • 没有构造函数、析构函数(析构函数功能会有其他的替代方案)
  • 不支持嵌套函数、嵌套类、嵌套命名空间等。
  • 没有命名空间(有其他的替代机制)
  • 不支持宏
  • 不支持模板约束,模板元编程。
  • 不支持union类型。
  • 不需要前向声明
  • 没有static的三层意思。
  • 别名定义只能在顶部的import语句中。
  • 可变性是简单的final,没有C++那么复杂的规则。
  • 没有友员机制,默认internal访问级别。
  • 不支持多继承,不支持私有继承
  • ++i和i++只保留一个,并且不返回任何值。
  • 不支持switch语句贯穿
  • 不支持do while, goto语句。

其他

后面还会有闭包和运算符重载等,不再详细写,其他语法参考C++。
基本就是C+OO+GC。没有各种语法糖、没有怪异语法。
最后,heap语言只是开始设计和征求意见,并没有开始开发。
回复

使用道具 举报

2

主题

4

帖子

8

积分

新手上路

Rank: 1

积分
8
发表于 2022-9-21 14:17:25 | 显示全部楼层
评价一下rust?
回复

使用道具 举报

0

主题

2

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2022-9-21 14:17:32 | 显示全部楼层
为了安全和性能,牺牲了可读性。不支持继承多态。
回复

使用道具 举报

2

主题

4

帖子

8

积分

新手上路

Rank: 1

积分
8
发表于 2022-9-21 14:17:59 | 显示全部楼层
B* 是何意呢? int* p 和 int *p 有区别吗?
回复

使用道具 举报

2

主题

5

帖子

9

积分

新手上路

Rank: 1

积分
9
发表于 2022-9-21 14:18:57 | 显示全部楼层
提几个问题1. 在这种简单的指针生存期模型下如何解决迭代器失效和自引用等问题?2. 如果应用逃逸分析,那区分heap指针和普通指针的意义何在?3. 引入继承和泛型后如何解决型变的问题?比如将一只猫当成动物传递给某函数,结果它被替换成了一只狗[doge],函数返回后调用者把狗当成猫用,原地爆炸。
回复

使用道具 举报

1

主题

2

帖子

4

积分

新手上路

Rank: 1

积分
4
发表于 2022-9-21 14:19:26 | 显示全部楼层
谢谢。1.没有迭代器,数组通过整数索引访问。自引用会导致什么问题?。2.如果逃逸分析能100%准确,那么我的设计确实是多次一举。当初主要是觉得不应该像Go一样偷偷改变内存分配方式,但这其实只需要报错即可。
3.和C++一样,不支持协变逆变。
回复

使用道具 举报

0

主题

1

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2022-9-21 14:19:48 | 显示全部楼层
我现在能想到的一个问题是,逃逸分析在虚函数调用时没法准确,因为虚函数运行时才确定。
回复

使用道具 举报

0

主题

2

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2022-9-21 14:20:02 | 显示全部楼层
没区别。为了好理解一点,我把示例代码都改成int* p了。
回复

使用道具 举报

0

主题

2

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2022-9-21 14:20:21 | 显示全部楼层
自引用的问题:1. 创建局部变量 A a2. 把a的指针塞进a3. 返回a的值,a的指针悬空rust是不接受这种写法,只能用 unsafe 写。我又想到一个生存期提升的问题1. 有函数参数A* a2. 创建局部变量 B b3. 调用子函数 foo(a, &b)4. foo把b的指针塞进a5. 父函数返回,b悬空
回复

使用道具 举报

1

主题

4

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2022-9-21 14:20:51 | 显示全部楼层
第二个问题是参数自引用,是不允许的。自引用问题我确实没有想到,广泛来说是返回值提升了生命期。需要编译器做更多返回值检查,例如检查结构体里面是否包含非heap指针。这些情况报错就好了,我不想为了性能加太多unsafe,容易失控。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表