资讯动态

Rust 编译器 const 泛型内部实现指南:ConstKind、匿名常量与 ConstArgHasType

发布时间:2026/9/10 14:45:06 来源:尧图企业网站定制
Rust 编译器 const 泛型内部实现指南ConstKind、匿名常量与 ConstArgHasType【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust导读本文以 Rust 编译器开发指南中的 const-generics 章节 为核心骨架结合当前仓库中rustc_type_ir、rustc_hir_analysis、rustc_trait_selection、rustc_middle等 crate 的源码实现系统讲解ty::Const在类型系统中的表示、匿名常量anon consts的脱糖机制、禁止匿名常量使用泛型参数的三重防线以及ConstArgHasType如何从类型系统层面保证常量参数的类型正确性。读完本文你将理解 const 泛型参数从 HIR 到类型系统表示的完整内部流程以及min_const_generics、generic_const_parameter_types、min_generic_const_args等特性在编译器内部是如何落地的。常量参数的类别ty::Const与ConstKindconst 泛型在类型系统中的核心表示是ty::Const其具体变体定义在ConstKind枚举中源码位于 compiler/rustc_type_ir/src/const_kind.rs。文档开篇指出大多数ConstKind变体都能在类型系统中找到直接对应的平行物例如ConstKind::Param等价于TyKind::Param。这种一一对应关系使得 const 泛型的处理可以大量复用类型系统已有的基础设施。当前源码中ConstKind的完整变体如下变体含义类型系统中的平行物Paramconst 泛型参数如NTyKind::ParamInfer推断变量值待推断类型推断变量Bound有界 const 变量trait 查询内部使用TyKind::BoundPlaceholder占位符全称量化的高阶 constTyKind::PlaceholderAlias未归一化的 const 项anon const、关联 const、自由 const 项TyKind::AliasValue单态化之后的最终求值结果TyKind::Str、TyKind::ADT等具体类型Error无法计算的占位避免无用的级联报错TyKind::ErrorExpr非 const 项的表达式feature(generic_const_exprs)专用无直接平行物两个最值得关注的变体ConstKind::Alias即文档所述的Unevaluated文档写作时它叫ConstKind::Unevaluated并明确指出长期应重命名为Alias同时引入AliasConstKind与ty::AliasKind平行。这一前瞻性设计在当前仓库中已经落地源码中变体已正式更名为ConstKind::Alias(ty::IsRigid, ty::AliasConstI)并且AliasConstKind枚举见 compiler/rustc_type_ir/src/const_kind.rs包含了与AliasTyKind平行的五个子类Projection形如Type as Trait::AssocConst的投影InherentSelf/InherentImplinherent impl 中的关联常量Free自由 const 项Anon匿名常量AliasConst提供了type_of方法compiler/rustc_type_ir/src/const_kind.rs通过DefId查type_of查询并实例化参数这一能力正是后面ConstArgHasType类型检查的基础。ConstKind::Value这是ty::Const在单态化之后的最终值类似完全具体的TyKind::Str或TyKind::ADT。它内部直接保存了自身的类型源码中通过cv.ty访问因此在做类型检查时可以平凡地直接取出类型。推断变量与 const 参数的处理文档特别指出推断变量ConstKind::Infer在几乎所有地方都与类型推断变量等价处理非常无趣const 参数ConstKind::Param也几乎等价于类型参数的使用。真正的微妙之处在于它们在解析、名称解析name resolution和 AST lowering阶段的处理差异详见 rustc 开发指南的姊妹章节 ambig-unambig-ty-and-consts。匿名常量Anon Consts任意表达式进入 const 泛型的通道匿名常量anon consts即 anonymous const items 的简称是 const 泛型独有的概念类型系统中没有对应物。它是任意表达式被表示为 const 泛型实参的方式——例如数组长度1 1、函数调用foo()甚至仅仅一个0。struct Fooconst N: usize; type Alias [u8; 1 1];其中数组长度的 const 实参1 1就是一个 anon const。脱糖Desugaring语义在概念上anon const 会被脱糖为一个真实的 const 项struct Fooconst N: usize; const ANON: usize 1 1; type Alias [u8; ANON];注意[u8; ANON]中的数组长度本身并不是一个包含ANON使用的 anon const而是对ANON这个 const 项的直接使用即ConstKind::Alias/ 原Unevaluated。关键的脱糖约束是anon const 不继承所在 item 的任何泛型参数struct Fooconst N: usize; type AliasT: Sized [T; 1 1]; // Desugars To; struct Fooconst N: usize; const ANON: usize 1 1; type AliasT: Sized [T; ANON];可以看到即便Alias既有类型参数T又有 where 子句T: Sized脱糖后的ANON依然没有任何泛型参数和 where 子句。这种脱糖正是anon const 不能使用泛型参数这一规则的实现机制之一。为什么编译器不能真正脱糖为 HIR item虽然用真实 const 项来思考脱糖很方便但编译器并没有按这种方式实现原因在于AST lowering 阶段尚不知道 anon const 的类型无法脱糖成一个显式写出类型的真实 HIR item。文档给出的近似伪代码是struct Fooconst N: usize; type Alias [u8; 1 1]; // sort-of desugars to pseudo-rust: struct Fooconst N: usize; const ANON 1 1; // 注意没有显式类型 type Alias [u8; ANON];为了解决这个问题编译器引入了专门的数据结构DefKind::AnonConst和hir::Node::AnonConst用来表示这些无法真正脱糖的匿名 const 项。type_of查询与 query feedinganon const 的类型可以从type_of查询获得但有个反直觉的细节type_of查询本身并不包含计算 anon const 类型的逻辑直接调用会 ICE。真正的类型计算发生在HIR Ty lowering阶段它负责为所有被 lower 的 anon constfeeding注入type_of查询的值——通过查看该 anon const 所对应 const 参数的类型来推导其自身类型。这一逻辑在源码lower_const_arg中清晰可见compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs当 const 实参是hir::ConstArgKind::Anon时函数会先检查待注入类型是否含自由 region、推断变量或未替换的泛型分别报出 anonymous constants with lifetimes/inferred types/generics are not yet supported 三类错误最终通过tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(tcx, ty))把类型注入type_of查询。以type Alias [u8; 1 1];为例当 HIR ty lowering 处理Alias的数组类型时会顺带 lower 数组长度并 feedtype_of(ANON) - usize。这等效于在编译流程的稍后阶段才设置ANONconst 项的类型而非在构造 HIR 时设置。最终的类型系统表示经过上述全部脱糖后anon const 在类型系统中的最终表示是一个ConstKind::Alias其DefId指向该AnonConst源码中对应AliasConstKind::Anon { def_id }见 compiler/rustc_type_ir/src/const_kind.rs。这与在启用min_generic_const_args时直接表示一个真实 const 项用法的方式完全一致——不经过 anon const 时同样是Alias表示。这种设计带来两个收益const 别名的表示与TyKind::Alias完全同构类型系统可以统一处理拥有真正的 HIR body 意味着大量代码复用可以复用 HIR 类型检查、所有到 MIR 的 lowering 步骤最终复用 const eval常量求值。禁止使用泛型参数的三重防线anon const 不能使用泛型参数是通过三种独立机制共同保证的缺一不可防线 1名称解析Name Resolution不解析泛型参数路径名称解析阶段不会把 anon const 内部的路径解析到泛型参数上// *1* Errors in name resolution type Aliasconst N: usize [u8; N 1]; //~^ ERROR: generic parameters may not be used in const operations防线 2HIR Ty lowering 拒绝Self类型别名引用泛型参数// *2* Errors in HIR Ty lowering: struct FooT(T); implT FooT { fn assoc() - [u8; { let a: Self; 0 }] {} //~^ ERROR: generic Self types are currently not permitted in anonymous constants }这条防线特别微妙——文档指出 HIR Ty lowering 很容易写错而未能正确执行该检查且现有检查过于保守会意外放行一些泛型参数进入 anon const body对应 tracking issue #144547。错误地允许泛型参数进入 anon const轻则导致 ICE重则接受病态ill-formed程序。防线 3generics_of不包含父项不继承 where 子句与泛型// *3* Errors due to lack of where clauses on the desugared anon const trait TraitT { const ASSOC: usize; } fn fooT() - [u8; ()::ASSOC] //~^ ERROR: no associated item named ASSOC found for unit type () where (): TraitT {}这一条同样微妙由于 anon const 不继承父 item 的任何 where 子句trait 求解器就不可能基于作用域内提及泛型参数的 where 子句把推断变量推断到泛型参数上。例如从表达式() as Trait?x::ASSOC和 in-scope where 子句(): TraitT推断出?xT这种情况就被杜绝了。此外一旦真的意外放行泛型参数进入 anon const由于 anon const 环境中缺少处理这些泛型参数所需的全部信息编译器几乎必然 ICE 或至少顺带报错——这反而提高了错误被及早发现的概率。该机制在源码中有直接印证generics_of查询对Node::AnonConst的处理compiler/rustc_hir_analysis/src/collect/generics_of.rs会根据tcx.anon_const_kind(def_id)决定父泛型AnonConstKind::MCG稳定的min_const_genericsanon const→ 返回None不继承任何泛型AnonConstKind::RepeatExprCount数组重复表达式计数→ 返回Some(parent_did)作为向后兼容 hack 继承父泛型详见下一节GCE anon const 同样不允许使用泛型参数否则一旦脱糖就会产生前向声明式使用如const N: [u8; ANON::N]。AnonConstKind枚举的完整定义位于 compiler/rustc_type_ir/src/const_kind.rs共五个变体GCE、MCG、RepeatExprCount、NonTypeSystemAnon、NonTypeSystemInline。唯一的例外数组重复表达式Array Repeat Expressions上述所有规则有一个例外数组重复表达式的计数。作为向后兼容 hack编译器允许 repeat count 这个 const 实参使用泛型参数fn fooT: Sized() { let a [1_u8; size_of::T()]; }size_of::T()虽然语法上引用了类型参数T但它对任何T都能求值——裸指针的大小与具体类型无关64 位平台恒为 8 字节。正如文档所说从某种意义上我们只允许泛型参数在语义上未被使用时出现。为了避免放行泛型参数进入 anon const 的大部分问题编译器要求这类常量必须在单态化之前例如类型检查期间完成求值。当检测到语法上包含泛型参数但求值实际不依赖它们的 anon const 被成功求值时编译器会发出const_evaluatable_uncheckedfuture-compat 警告FCW对应 rust-lang/rust#76200 所跟踪的问题。该 FCW 的意图是在未来稳定更多 const 实参中使用泛型参数的方式例如min_generic_const_args或现已搁置的generic_const_exprs之后将其升级为硬错误。该 FCW 的实现位于const_eval_resolve_for_typeck查询中源码见 compiler/rustc_middle/src/mir/interpret/queries.rs。该查询接收TypingEnv与ty::AliasConst其行为要点包含非 region 推断变量的常量无法解析会直接bug!这种情况应改用Infcx::const_eval_resolve对AliasConstKind的Projection/InherentImpl/Free/Anon四类取出DefId尝试解析出Instance后构造GlobalId进行求值解析失败TooGeneric时故意不指向常量的使用位置因为对于这类错误使用位置无关紧要。与generic_const_parameter_types的不兼容性支持const N: [u8; M]或const N: FooT这类类型引用泛型参数的 const 参数与当前 anon const 架构并不兼容原因有二原因 1anon const 的类型也不能引用泛型参数既然 anon const 不能使用泛型参数其类型同样不能引用泛型参数。这意味着从根本上无法用 anon const 作为类型仍引用泛型参数的 const 参数的实参#![feature(adt_const_params, generic_const_parameter_types)] fn fooconst N: usize, const M: [u8; N]() {} fn barconst N: usize() { // There is no way to specify the const argument to M foo::N, { [1_u8; N] }(); }{ [1_u8; N] }引用N本身就是一个 anon const而 anon const 不能引用泛型参数因此根本无法写出M的合法实参。原因 2lowering 时类型可能尚未完全确定当前要求在 HIR ty lowering 时就确定 anon const 的类型而泛型 const 参数类型的场景下当前已知类型可能包含推断变量#![feature(adt_const_params, generic_const_parameter_types)] fn fooconst N: usize, const M: [u8; N]() {} fn bar() { // The const argument to N must be explicitly specified // even though it is able to be inferred foo::_, { [1_u8; 3] }(); }尽管N本可以从[1_u8; 3]的长度推断出来但实参仍必须显式写出。这一限制的根源正是前面lower_const_arg中的检查fed 进type_of查询的结果不允许包含推断变量否则会 ICE见 compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs。未来方向文档指出如何让generic_const_parameter_types与其余 const 泛型机制和谐共处目前仍无定论generic_const_exprs本可允许 anon const 的类型引用泛型参数但该设计最终被证明不可行min_generic_const_args允许部分表达式例如数组构造不经过 anon const直接表示从而绕开上述两类问题——但它是否足够仍待验证。常量参数的类型检查ConstArgHasType一个 const 实参要良构well-formed其类型必须与所对应 const 参数的类型一致。例如bool类型的 const 实参不能作为数组长度因为数组长度参数类型是usizetype Aliasconst B: bool [u8; B]; //~^ ERROR:机制为每个 const 参数脱糖出ConstArgHasType子句编译器引入ClauseKind::ConstArgHasType(ty::Const, Ty)来承载这一检查定义见 compiler/rustc_type_ir/src/predicate_kind.rs注释明确写着 Ensures that a const generic argument to a parameterconst N: u8is of typeu8。对 item 上定义的每一个const 参数编译器都会在其 where 子句列表中脱糖出一条等价的ConstArgHasType子句fn fooconst N: usize() {} // desugars to in pseudo-rust fn fooconst N() where // ConstArgHasType(N, usize) N: usize, {}这样每当通过证明该 item 的全部子句来检查良构性时就会顺带验证所有 const 实参类型是否正确。ConstArgHasType是健全性soundness关键的类型检查通道。证明ConstArgHasTypegoal 的实现证明ConstArgHasTypegoal 的过程分两步先计算 const 实参的类型再与给定类型做 equating合一。文档给出了三种典型情形的类型计算路径ConstKind::Param(N)在ParamEnv中查找ConstArgHasType(N, ty)子句ConstKind::Value类型直接存在值内部可平凡取出ConstKind::Alias原Unevaluated调用type_of查询计算。这些路径在旧 trait solver 的evaluate_predicates_recursively中有完整实现compiler/rustc_trait_selection/src/traits/select/mod.rs对ConstKind::Infer返回EvaluatedToAmbig对Value(cv)直接取cv.ty对Alias(_, alias_const)调用alias_const.type_of(tcx)对Param(param_ct)调用param_ct.find_const_ty_from_env(obligation.param_env)从环境中查找最后统一执行infcx.at(...).eq(ct_ty, ty)完成类型合一。间接检查路径通过 feedtype_of顺带完成除ConstArgHasType之外某些情况下编译器会间接地检查 const 实参类型type Alias [u8; true]; // desugars to const ANON: usize true; type Alias [u8; ANON];通过用 const 参数的类型来 feed anon const 的type_of就保证了涉及该 anon const 的ConstArgHasTypegoal 必然成功。当 anon const 的类型与 const 参数类型不一致时实际发生的是类型检查错误——在检查 anon const body 时报错。回到上面的例子[u8; ANON]本身是良构类型因为ANON的类型是usize但ANON的body是病态的——true无法作为usize类型的 const 项返回值于是产生类型检查错误。这正是类型系统层面类型正确、表达式层面类型错误被分开处理的精妙之处。小结本文梳理了 const 泛型在 rustc 内部的完整技术脉络ConstKind变体体系与类型系统一一平行Alias原Unevaluated与Value是其中最核心的两个变体anon const 脱糖承担任意表达式进入 const 泛型的任务通过DefKind::AnonConsthir::Node::AnonConsttype_ofquery feeding 实现三重防线名称解析、HIR ty lowering、generics_of不继承共同保证 anon const 无法使用泛型参数数组重复表达式是唯一的向后兼容例外由const_evaluatable_uncheckedFCW 与const_eval_resolve_for_typeck查询管控ConstArgHasType子句是检查 const 实参类型正确性的健全性关键配合type_offeeding 的间接检查路径构成完整闭环。相关源码与文档可继续深入阅读文档原文 const-generics.md、ConstKind定义 const_kind.rs、ConstArgHasType定义 predicate_kind.rs、anon const 类型注入 hir_ty_lowering/mod.rs、泛型继承控制 generics_of.rs、FCW 求值实现 queries.rs以及旧 solver 中的 goal 证明 select/mod.rs。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价