// defineFullInheritance copies any inherited members over to types, as well as type checking // for inheritance cycles. func (g *TypeGraph) defineFullInheritance(modifier compilergraph.GraphLayerModifier) { buildInheritance := func(key interface{}, value interface{}) bool { typeDecl := value.(TGTypeDecl) g.buildInheritedMembership(typeDecl, NodePredicateMember, modifier) g.buildInheritedMembership(typeDecl, NodePredicateTypeOperator, modifier) return true } // Enqueue the full set of classes with dependencies on any parent types. workqueue := compilerutil.Queue() for _, typeDecl := range g.TypeDecls() { if typeDecl.TypeKind() != ClassType { continue } // Build a set of dependencies for this type. var dependencies = make([]interface{}, 0) for _, inheritsRef := range typeDecl.ParentTypes() { dependencies = append(dependencies, inheritsRef.ReferredType()) } workqueue.Enqueue(typeDecl, typeDecl, buildInheritance, dependencies...) } // Run the queue to construct the full inheritance. result := workqueue.Run() if result.HasCycle { // TODO(jschorr): If there are two cycles, this will conflate them. We should do actual // checking here. var types = make([]string, len(result.Cycle)) for index, key := range result.Cycle { decl := key.(TGTypeDecl) types[index] = decl.Name() } typeNode := result.Cycle[0].(TGTypeDecl).GraphNode g.decorateWithError(modifier.Modify(typeNode), "A cycle was detected in the inheritance of types: %v", types) } }
// buildInheritedMembership copies any applicable type members from the inherited type references to the given type. func (t *TypeGraph) buildInheritedMembership(typeDecl TGTypeDecl, childPredicate compilergraph.Predicate, modifier compilergraph.GraphLayerModifier) { // Build a map of all the existing names. names := map[string]bool{} it := typeDecl.GraphNode.StartQuery(). Out(childPredicate). BuildNodeIterator(NodePredicateMemberName) for it.Next() { names[it.GetPredicate(NodePredicateMemberName).String()] = true } // Add members defined on the type's inheritance, skipping those already defined. typeNode := typeDecl.GraphNode for _, inherit := range typeDecl.ParentTypes() { parentType := inherit.ReferredType() pit := parentType.StartQuery(). Out(childPredicate). BuildNodeIterator(NodePredicateMemberName) for pit.Next() { // Skip this member if already defined. name := pit.GetPredicate(NodePredicateMemberName).String() if _, exists := names[name]; exists { // Ensure that the parent member is not a required field. If so, then we cannot // shadow the field. parentMember := TGMember{pit.Node(), t} if parentMember.IsRequiredField() { t.decorateWithError(modifier.Modify(typeDecl.GraphNode), "%v %v cannot compose %v %v as it shadows %v '%v' which requires initialization", typeDecl.Title(), typeDecl.Name(), parentType.Title(), parentType.Name(), parentMember.Title(), parentMember.Name()) } continue } // Mark the name as added. names[name] = true // Create a new node of the same kind and copy over any predicates except the type. parentMemberNode := pit.Node() memberNode := parentMemberNode.CloneExcept(modifier, NodePredicateMemberType) memberNode.Connect(NodePredicateMemberBaseMember, parentMemberNode) memberNode.DecorateWithTagged(NodePredicateMemberBaseSource, inherit) modifier.Modify(typeNode).Connect(childPredicate, memberNode) // If the node is an operator, nothing more to do. if memberNode.Kind == NodeTypeOperator { continue } parentMemberType := parentMemberNode.GetTagged(NodePredicateMemberType, t.AnyTypeReference()).(TypeReference) // If the parent type has generics, then replace the generics in the member type with those // specified in the inheritance type reference. if _, ok := parentType.GraphNode.TryGetNode(NodePredicateTypeGeneric); !ok { // Parent type has no generics, so just decorate with the type directly. memberNode.DecorateWithTagged(NodePredicateMemberType, parentMemberType) continue } memberType := parentMemberType.TransformUnder(inherit) memberNode.DecorateWithTagged(NodePredicateMemberType, memberType) } } }