Scala宏通过超类成员符号访问封闭对象时触发内部错误
Scala宏访问超类成员触发“无法找到外部访问器符号”错误的解决方法
问题重现
注:原代码存在笔误,object Sub应继承Base而非ApplicationInfo,否则无法获取appInfo方法,修正后的代码如下:
case class ApplicationInfo() abstract class Base { def appInfo = new ApplicationInfo() } object Sub extends Base { def foo = showInfo }
触发错误的宏实现:
def showInfoImpl(c: blackbox.Context) = { import c.universe val ai = Iterator.iterate(c.internal.enclosingOwner) { _.owner } .find { _.isClass } .getOrElse(c.abort(c.enclosingPosition, "Cannot find enclosing class")) .info .member(TermName("appInfo")) q"$ai" }
编译时会抛出错误:
Internal error: unable to find the outer accessor symbol of object Sub
问题原因
直接从类的info中提取超类成员的Symbol并插入语法树时,编译器无法将这个孤立的符号与当前实例的上下文正确绑定。info返回的是类的原始符号信息,不包含当前对象的外部访问器关联逻辑;而直接写q"appInfo"或通过子类本地方法中转时,编译器会走正常的作用域查找流程,自动处理超类成员的访问上下文。
解决方法
不要直接引用从info中获取的Symbol,而是基于当前实例的类型生成正确的成员选择树,让编译器自动处理上下文关联:
def showInfoImpl(c: blackbox.Context) = { import c.universe._ val enclosingClass = Iterator.iterate(c.internal.enclosingOwner)(_.owner) .find(_.isClass) .getOrElse(c.abort(c.enclosingPosition, "Cannot find enclosing class")) // 获取包含继承关系的类完整类型 val classType = enclosingClass.asType.toType val appInfoSym = classType.member(TermName("appInfo")) match { case NoSymbol => c.abort(c.enclosingPosition, "appInfo not found in enclosing class or its supertypes") case sym => sym } // 生成带this前缀的成员选择树,确保上下文关联 q"${c.prefix}.this.$appInfoSym" }
关键要点
- 用
asType.toType获取类的完整类型,而非直接使用info,这样能包含所有超类的成员信息。 - 生成以
this为前缀的成员选择树,让编译器自动处理超类成员的访问逻辑与上下文绑定。 - 如果只是简单引用成员,直接生成标识符
q"appInfo"是最简洁的方式,仅在需要动态查找成员时,才需要通过类型获取符号并生成选择树。
内容的提问来源于stack exchange,提问作者Dima
相关产品推荐
相关产品推荐

