Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parsing and type-checking generics #671

Open
wants to merge 39 commits into
base: master
Choose a base branch
from

Conversation

koflin
Copy link

@koflin koflin commented Aug 24, 2023

These are my changes to support parsing and type-checking generics.

- generated gobra parser code
- added Ast nodes for type parameters and type arguments
- modified parse tree translator
- modified parser and parse tree translator accordingly
- implemented ambiguity resolution for indexed expression (normal index expression or type arguments)
- add tests
- fix some issues in the parse tree translator and the pretty printer
@koflin
Copy link
Author

koflin commented Aug 24, 2023

@Felalolf

These are the merged changes.

First of all, the unit tests fail because of an issue I already mentioned. It has to do with this PR (#634) It somehow breaks some of my unit tests. It always complains that
Logic error: Expected that package pkg has access to the type information of package BuiltInImport
I believe that replacing new TypeInfoImpl(tree, context)(config) with new TypeInfoImpl(tree, Map.empty)(config) in the ExprTypingUnitTests.scala causes this issue. I don't know how to fix this.

Second, the GobraTests of the integration suit is stuck. I also don't know what the issue is here. Before merging everything worked fine.

Note that before the merge with the current master everything was working fine which can be seen here: https://github.com/koflin/gobra/tree/generics-before-merge.

Copy link
Contributor

@Felalolf Felalolf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments

@@ -0,0 +1,606 @@
// Generated from s:\GitHub\gobra\src\main\antlr4\GoLexer.g4 by ANTLR 4.9.2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this file

@@ -2775,6 +2780,7 @@ object Desugar extends LazyLogging {
for {
dArgs <- sequence(args.map { x => option(x.map(exprD(ctx, info)(_))) })
idT = info.typ(base) match {
// TODO handle this
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handle what? please make the comment more concrete.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was a comment for myself, I'll remove it

@@ -22,6 +22,7 @@ trait UnderlyingType { this: TypeInfoImpl =>
lazy val underlyingType: Type => Type =
attr[Type, Type] {
case Single(DeclaredT(t: PTypeDecl, context: ExternalTypeInfo)) => underlyingType(context.symbType(t.right))
case Single(TypeParameterT(_, t: PInterfaceType, ctx)) => underlyingType(ctx.symbType(t))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks as if the underlying type of a type parameter is the underlying type of its constraint. Is this intended? Why is this correct?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the underlying type of a type parameter is the underlying type of its type constraint (interface) https://go.dev/ref/spec#Underlying_types

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then please add a comment

case _ => false
}
}

def isTypeParameter(t: Type): Boolean = t match {
case TypeParameterT(_, _, _) => true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
case TypeParameterT(_, _, _) => true
case _: TypeParameterT => true

@@ -35,6 +35,9 @@ trait AmbiguityResolution { this: TypeInfoImpl =>
case _ => Left(n)
})

case n: PIndexedExp =>
if (exprOrType(n.base).isLeft) Left(n) else Right(n)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a comment

Comment on lines +246 to +268
override def withChildren(children: Seq[Any], pos: Option[(Position, Position)], forceRewrite: Boolean): this.type = {
assert(pos.isEmpty, "The pos argument must be set to nil if called on Gobra nodes.")

if (!forceRewrite && this.children == children) {
this
} else {
create(children)
}
}

private def create(children: Seq[Any]): this.type = {
import scala.reflect.runtime.{universe => reflection}
val mirror = reflection.runtimeMirror(reflection.getClass.getClassLoader)
val instanceMirror = mirror.reflect(this)
val classSymbol = instanceMirror.symbol
val classMirror = mirror.reflectClass(classSymbol)
val constructorSymbol = instanceMirror.symbol.primaryConstructor.asMethod
val constructorMirror = classMirror.reflectConstructor(constructorSymbol)

// Call constructor
val newNode = constructorMirror(children: _*)
newNode.asInstanceOf[this.type]
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

viper.gobra.ast.internal.Node needs this to work correctly. It is taken from viper.gobra.ast.internal.Rewritable and modified slightly

import scala.annotation.tailrec
sealed trait TypeSet

object TypeSet {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add more documentation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean for the entire file

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, sorry. I forgot this one

@@ -160,6 +166,29 @@ trait TypeTyping extends BaseTyping { this: TypeInfoImpl =>

case _ => violation(s"expected type, but got $n")
}

case n: PIndexedExp =>
resolve(n) match {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also deal with unexpected matches (via violation).

Comment on lines 734 to 736
val typeMap = f.args.filter(isTypeParameter).zip(fc.args.map(exprType)).map {
case (TypeParameterT(id, _, _), typ) => (PIdnDef(id.name), typ)
}.toMap[PIdnDef, Type]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure this is correct. If only the second argument is a type parameter, would you then not zip the second parameter with the first argument?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes you are right, I'll correct this

Comment on lines 358 to 360
val typeMap = args.filter(isTypeParameter).zip(argTypes).map {
case (TypeParameterT(id, _, _), typ) => (PIdnDef(id.name), typ)
}.toMap[PIdnDef, Type]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please refactor the type inference into a separate method

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants