总结
替换D的逃逸分析
Rikki
说,他一个月前曾与Dennis
讨论过简化D的逃逸分析
,但没有结果.在BeerConf
上,他再次提起了它,Dennis
说他一直在考虑它
.
Rikki
也与Walter
谈过这件事,Walter
曾说过DIP1000
并没有完全如期工作,且有点太复杂了
.
因此,Rikki
想讨论按D逃逸分析方法
替换DIP1000
的可能性.在做出可靠的决定
前,第一步是确保它完全在预览开关之下
.丹尼斯
证实当前是这样
.
Rikki
说,下一步是考虑切换它
及它可能的样子
.他请求建议.
Dennis
说,在决定如何切换它
前,应该首先说明
当前设计原因及切换的目标
.他说他有一些问题
.一个是缺乏传递域
.
另一个问题
是,即使结构有多个成员
,它们也只有一个生命期
.他一直在考虑允许按域
注解结构字段
,但他还没有具体的提议
.
Walter
说,他遇见的困难不是DIP1000
复杂,而是语言复杂
.必须适配语言的各种构造
.引用类型工作原理
或隐式类类型
或懒参数构造器
这就是复杂性的来源
.
他给出了成员函数
的隐式本
参数的示例.如果有人想了解如何在构造器或成员函数
中使用DIP1000
,他一遍遍地解释
说,则只需要按显式参数
一样,把它写出来
.
然后就能看到它应该是如何操作的
.但人们发现这无休止地困惑
.
简化它的提案
还必须证明它如何可能
比现在的DIP1000
更简单,因为DIP1000
很复杂,因为语言很复杂
.它必须支持每个语言结构
.
Rikki
说,逃逸分析
分为三个不同级
.最基本级
是"这是
一个而它帮助输出
的输出
".然后你就有了语言可推导出的东西
.
然后就有了你可添加
的可证明的东西
.现在真没有那个规模
,所以现在当事情太宽泛
时,没有逃避的机会
.
walter
重申,DIP1000
的复杂性
是因为语言的复杂性
,而不是概念自身
,这非常简单
.如果用指针写出来
,一切都会很清晰和简单
.
当你添加像auto ref
此东西时,它开始复杂.他从来不喜欢auto ref
,也从没用过它
,因为它太怪了
.
他说,如果Rikki
能想出更好
方法来完成,他会支持.DIP1000
是他最好的机会
.
Timon
说,DIP1000
可以说已承担了
一些可注解不同级
间接的复杂度成本
,但一般禁止这样.应有更好的权衡
.
Walter
说有两种间接
:指针和引用
.这使得DIP1000
的复杂性
增加了一倍.Timon
同意,但表示即DIP1000
不是你在按指针转换所有内容
时得到的结果
.
DIP1000
比这更进一步,因为实际上每个指针
都有两级间接性
,但不是按正交
限制它.根据构造
注解任一间接级
.
Walter
同意,并说这是因为引用
有隐式的间接性
,而指针
则没有.他问可做些什么
.
Rikki
让每个人都说他们的想法
.
Quirin
说,他理解DIP1000
的目标
是你可取局部变量的地址
,比如静态数组
等,且按域限定指针
且无法逃逸
.
因此,在DIP1000
为默认值
的未来版本
中,可能会有个编译器开关
来禁止它
,这样禁止取局部变量的地址
.
他说他遇见的问题是
,如果有系统函数
,但实际上没有按@系统
注解它,然后有域注解
,编译器会认为你正确处理了域操作
.
但如果你不是
,你就完蛋了
.而意外很容易
发生.
这是DIP1000
的一个问题.你可在系统代码
中坑自己
.如果不是在安全代码
中做对了
,但如果是初学者
,没有用@安全
注解某些东西,然后使用了它,如,是隐式域
的-preview=in
,就会遇见麻烦
.
所以可有选项
禁止它们,但在代码
中允许像@安全
一样的所有域检查
等会很好.
沃尔特说,@系统
关闭了所有检查
,因为有时需要做一些烦人事情
.初学者
不应编写@系统
代码.Quirin
说,如果不使用@safe
,DIP1000
会使该语言更加危险
.
这就是有些人
搞不来的原因.
Walter
认为最大的问题
是人们不喜欢写注解
.只在没有函数体
时,它们是必需
的.如果只是取本地地址
,编译器会说,“好的,现在是域指针
了”.
它会自动
这样.困难
在于有两个地方
需要添加注解
:没有函数体
时和在虚函数中
.这时,编译器无法自动这样
.
Walter
说,语言最近有了变化
,允许对局部变量
使用引用
.这允许无需注解
,更高的安全性
.这是一件好事
.它通过减少原始指针的需求
来改进语言
.
下一步
是允许构字段
上的引用
.必须
弄清楚它的语义
,但是越能改进语言
以减少使用原始指针
,它自身就越安全
,问题就会越少
.
Adam
说有人建议不要在打开DIP1000
时构建Phobosv3
.他有点同意该观点.他前曾告诉Walter
,DIP1000
不值得.
Rikki
想说明,如果没有引用计数
,基本上不可能
每秒处理100,000
个请求.这是由Walter
在物主逃逸分析
方面的工作所控制
的,而这反之又以逃逸分析
为主题的.
所以他在这方面被阻塞了
,因此想要排序逃逸分析
.
Walter
说,ROI
如此之低的原因是,因为栈中
的错误指针
,人们很少在程序中
遇见易错漏洞.Mathias
问为什么花这么多时间
在上面.
沃尔特
比作飞机失事
:它们很少见
,但一旦发生,就会造成灾难性的后果
.你不可能既是内存安全
的语言,又这样
.
Mathias
说,DIP1000
使他不想
使用D
,因为当他使用vibe.d
允许D时,他得到了大量的弃用
.这太可怕了
.他希望默认不打开
.
当谈到DIP
自身时,他说该组合
就是不管用
.需要他在类型定义
中使用域
注解他的类或结构
的设计在到达时都死了.
他说,很多人把它与常
比较,这是错误的比较
.常
是由外到内
,但域
是由内到外
.因此,如果你的外层
是常
且你组合
了一个有多个层
的类型,则你的所有层
都是常
.
对域
,情况正好相反.无法在语言
中表示域的深度
.这在语法
上是不可能
的.它就是无法工作
的.
更新:稍后开会并决定需要做两件事
来向前步进
:编制
一份失败的DIP1000
案例列表
,看看是否可解决它们;并考虑如何默认推导
.
把std.math
移动到core.math
Martin
说他多年来
一直想将std.math
移动到core.math
.很久以前,在GitHubPR
中与Walter
的讨论中提到了它
,他记得Walter
也同意它.
最近,再次
试使编译器测试包
独立于Phobos
.现在,现在DMD
和运行时
在同一个仓库
中,因此所有make
目标都是不依赖Phobos
而独立
的,这样运行编译器测试
,那就太好了
.
他做了实验
,发现测试用例
中的大多数Phobos
导入都是std.math
.一个常见原因
是幂符号^^
.还有一些测试
测试了数学内置函数
.
CTFE
的编译器使用混杂的函数名
,检测到调用标准数学函数
.这已是个问题,因为当更改std.math
中的属性
时,因为新混杂名
,也需要更新编译器
.
因此,测试了所有这些方法是否有效
,而CTFE
数学结果符合期望
.因此,有隐性依赖Phobos
.
Phobos
会导入所有内容
并转发
到core.math
,它已在运行时
中存在.它当前有大约五个函数
.
LDC
已转发了一些数学函数
.std.math
是为数不多的LDC
和GDC
只是为了可使用内联函数
,而有一些修改
的Phobos
模块之一.
移动进运行时
会更好,因为可最小化或清除
,Phobos
分叉的需求.
Walter
说std.math
类似一个包含很多东西
的手提袋
.他建议将应该是core.math
的内容移动到d运行时
中并转发
到这里,然后按使用core.math
更改测试包
.
他想保存std.math
.仍有很多编译器测试包
中不需要的数学函数
,可在那里保存它们
.
Jonathan
说,过去当决定真想要在d运行时
中导入Phobos
中的东西,但真想人们导入Phobos
时,就把该东西移到了core.internal
.
如,std.traits
导入了core.internal.traits
以避免重复d运行时
中使用的特征
,用户仍可通过std.traits
来取它.
实际上,std.math
中的大多数函数
都是由编译器
检测到的.
据他所知,std.math
是相当孤立
的,不依赖Phobos
中的其他东西
.他会仔细检查
,但他确信
,所以可把它移过来
.
他真不想
分开它.如果它在运行时
,则直接从那里包含
是合乎逻辑
的,从某个特定的编译器版本
开始,并保存它,在PhobosAPI
中呆一段时间
以实现后向兼容
.
所以最终位置
将在core.math
中.
Walter
说,很简单:如果想在编译器测试包
中放置它
,它需要进入运行时
.Martin
说他需要检查
,但这会是大部分函数
.
Mathias
认为尽管不能解决Martin
的问题,但应该去掉幂符号
.Martin
说,移动它到运行时
,可清除在未导入std.math
时试用它
时出错
的特例.
沃尔特
同意马蒂亚斯
的意见,认为应该淘汰它
.这是丑陋
的.
Adam
说,Phobos3
是改变
的绝佳机会
.这是自然的分界线
.可保持标准库2
的原样
并长期支持它
,但Martin
可在标准库3
中为所欲为
.
Adam
已在看std.math
并思考
他有多害怕移植它
.因此,如果Martin
想出其他东西
并告诉他如何让它有效
,他就会让它有效
.
主要类型语法DIP
基本思想
是,不更改语义
等修改语法
.它为了确保可由错误消息
表示的任意类型
,(如)也可在代码
中表示,且你不会收到解析错误
.
Quirin
说他已为该提案
提供了实现,且按期望
实现引用
.他已试了很久,并真正试突破了一些极限
.他没有发现问题
.
他说,同样问题
也适合链接
.像一个带extern(C)
链接的函数指针
.
他说也许Quirin
已解决了它,但要求他检查一下语法和括号问题
,并确保提案
没有这些问题
.
Walter
说,在编译器测试包
上试它
将很好.奎林同意
.
“让printf
安全的DIP
”
Iain
说,现在是2024
年,人们
仍在发明新的CPU
.他说中国人
发明了他们
自己的MIPSCPU
,他们不得不把旧的GDC
版本拖出来
,并移植
到他们的CPU
上,只是为了让LDC
和DMD
可正常工作
.
这是另一个
崭露头角的现代芯片(龙芯)
.让这些人
对拥有现代版本
的D编译器
而不是C++
编译器满意,这样他们就可跳到最新版本
.
因此,较旧的bootstrap
版本完全无价值
.
沃尔特说好.确保D编译器源码
的安全
并不重要.这只是他想做的事情
.但是,如果会导致很多下游问题
,则当然
,我们还能做什么?
Iain
说必须让文档非常响亮和显式
.GDC
在这方面做得很好
,它解释了如果从给定版本的编译器
开始,你必须做什么
,因为某些版本
的GDC
是使用特定的C++
标准编写
的.
要取得最新版本
,必须从起点
开始浏览这些版本
.也应该同意
,对DMD
同样.
Rikki
说明,Elias
已完成了LDC
的新docker版
镜像,该镜像完成了从LTS
版本到最新版本
的引导.他说应该可按C++
代码基转储编译器
,然后用来引导相同编译器版本
.
他已考虑了很久
了.这在今天
不是问题,但未来会变成一个问题.
使用printf
真很简单.这只是一个调用函数
:在栈上压几个参数
,调用一个函数
,完成.
Dennis
问,是否为DMD
创建了writeln
的最小模板版本
,因为DMD
大多只连接串
,偶尔
会构成一个整数.Walter
说可编写自己的printf
,但C标准库
中的那个是经过实战检验
,调试和优化
最多的.
Dennis
强调只需要连接串
.
Johanthan
建议把它包装起来
.
他同意Walter
所说的writeln
有问题,因为它是模板的暴风雪
.但他不断从人们那里听到不应删除这些模板
.
Walter
重申printf
受到了很大的恶意
,但它是历史上调试,优化
最多的函数.也许可实现一个只是调用安全
转发到printf
的writeln
.
它有它的问题,因此提交安全printf
提案.
他说Jonathan
是完全正确
的,模板
给writeln
带来了很多好处
.他不是在反对它
.但是在试调试编译器
时,处理writeln
是个巨大的痛苦
.
因此他总是
回到printf
的原因.他不想编译器
依赖writeln
,因为那样无法引导
编译器.
Jonathan
同意不想DMD
依赖标准库
.此时,也许用带串
并按C串
的东西转换它
来包装printf
是可行
的.
Walter
说这就是安全printf
提案的作用,它只是让编译器覆盖printf
式以使其内存安全
.Jonathan
说可避免直接使用包装器
调用printf
.
总之,编译器
与一般情况
不同.
空初化
一个引用
变量
Dennis
问大家是否都同意空
初化引用
变量应该是一个错误
.DIP
没有具体说明
,它没用例
.沃尔特说那是个错误
.没人反对,这里.
域和自动引用
Dennis
问大家是否都同意变量上的auto ref
关键字必须放在一起
,而不是与不同域内
的关键字
一起使用,如auto { ref int x = 3; }
沃尔特说是的,干掉它.
Quirin
说他注意到,在查看语法
时,动
和引用
并不总是需要彼此相邻
.如,可在参数列表
中编写ref const auto foo
.他建议应该禁止这样
.沃尔特说应该弃用它
.