0

I have been racking my brain over this for a while and I can't figure it out.

I have some kind of code like this in my c-like language:

struct PersonInfo {
    int numFriends;
    int favColour;
}

struct Person {
    PersonInfo info;
    string firstName;
    string lastName;
};

Person john;

john.info.numFriends; // <- specifically this part here!

How can I check that the path expression 'john.info.numFriends' is correct, i.e. all of the symbols exist. john exists, john is an instance of the structure Person, and we are accessing a field 'info' which exists, and also contains the field 'numFriends'.

What is a common algorithm/approach that is used to do this type of error check in compilers?

  • The way Python (as an interpreted language, admittedly, not a compiled one) does this is, every time a library or module is loaded into memory, it's assigned a dictionary of keywords to references. I believe that validating this would be like: (1) look at `Person`'s dict (2) does it have the key `info`? (3) Look at `PersonInfo`'s dict (4) does it have the key `numFriends`? So basically, just recursively check if the reference exists. Is there a reason that wouldn't work here? – Green Cloak Guy Mar 12 '19 at 23:40
  • @GreenCloakGuy This makes sense I just can't get the gist of putting this into code. I have a symbol table, but all that does is tell me if the symbol, e.g. 'john' exists. I guess I'd need to store the type of 'john' (Person) alongside the symbol? Also, do you know what the terminology for this is? Symbol resolution for a ... dot operator expression? – Alex Fengal Mar 12 '19 at 23:47

1 Answers1

2

The typing rule for this would look something like this: For any identifier memberName and any expression exp: If exp is an expression of type T where T is a struct with a member named memberName, then the expression exp.memberName is well-typed and has the type of T.memberName. Otherwise, the expression is ill-typed.

In terms of implementation, this isn't much different than implementing the other parts of the type checker: You recurse on the subexpression, check that it has an appropriate type, produce a type error if not and return the correct type otherwise. The only thing to consider is that you have to keep track of which struct types define which members. The code could look something like this:

TypeInfo visitDotExpression(MemberAccess exp) {
    TypeInfo leftType = visit(exp.leftOperand);
    if (leftType.isStructType()) {
        if (leftType.memberTypes.containsKey(exp.memberName)) {
            return leftType.memberTypes.get(exp.memberName);
        } else {
            // produce a type error because the struct doesn't have a member
            // with the given name
        }             
    } else {
        // Produce a type error because left operand of `.` is not a struct
    }
}
sepp2k
  • 363,768
  • 54
  • 674
  • 675