private void actuallyMakeStructFields() {
        if (doneStructFields) return;
        if (getFirstStructOrMapRecursive() == null) {
            doneStructFields = true;
            return;
        }
        var sdoc = repoDocType;
        var dataType = getDataType();

        java.util.function.BiConsumer<String, DataType> supplyStructField = (fieldName, fieldType) -> {
            if (structFields.containsKey(fieldName)) return;
            Matching subFieldMatching = new Matching();
            subFieldMatching.merge(this.matching);
            String subName = getName().concat(".").concat(fieldName);
            var subField = new SDField(sdoc, subName, fieldType, null,
                                       subFieldMatching, structFieldDepth + 1);
            structFields.put(fieldName, subField);
        };

        if (dataType instanceof MapDataType mdt) {
            supplyStructField.accept("key", mdt.getKeyType());
            supplyStructField.accept("value", mdt.getValueType());
        } else {
            if (structFieldDepth >= 10) {
                // too risky, infinite recursion
                doneStructFields = true;
                return;
            }
            if (dataType instanceof CollectionDataType) {
                dataType = ((CollectionDataType)dataType).getNestedType();
            }
            if ((dataType instanceof MapDataType) || (dataType instanceof CollectionDataType)) {
                // "array of map" or "array of array" will not have any struct fields
                // TODO: consider what this would mean
                doneStructFields = true;
                return;
            }
            SDDocumentType subType = sdoc != null ? sdoc.getType(dataType.getName()) : null;
            if (dataType instanceof TemporaryUnknownType && subType != null) {
                for (Field field : subType.fieldSet()) {
                    supplyStructField.accept(field.getName(), field.getDataType());
                }
            } else if (dataType instanceof OwnedTemporaryType && subType != null) {
                for (Field field : subType.fieldSet()) {
                    supplyStructField.accept(field.getName(), field.getDataType());
                }
            } else if (dataType instanceof StructDataType sdt) {
                for (Field field : sdt.getFields()) {
                    supplyStructField.accept(field.getName(), field.getDataType());
                }
            }
            if ((subType == null) && (!structFields.isEmpty())) {
                throw new IllegalArgumentException("Cannot find matching (repo=" + sdoc + ") for subfields in "
                                                   + this + " [" + getDataType() + getDataType().getClass() +
                                                   "] with " + structFields.size() + " struct fields");
            }
            // populate struct fields with matching
            if (subType != null) {
                for (Field f : subType.fieldSet()) {
                    if (f instanceof SDField sdField) {
                        SDField subField = structFields.get(sdField.getName());
                        if (subField != null) {
                            // we just made this with a copy of our matching (see above)
                            Matching subFieldMatching = subField.getMatching();
                            subFieldMatching.merge(sdField.getMatching());
                            subField.setMatching(subFieldMatching);
                        }
                    } else {
                        throw new IllegalArgumentException("Field in struct is not SDField " + f.getName());
                    }
                }
            }
            // else ("missing subtype for struct fields in: " + this + " type " + getDataType() + " [" + getDataType().getClass().getSimpleName() + "]");
        }
        doneStructFields = true;
    }
Returns true if (there is strong evidence that) the value stored by this field is bit-packed.
config-model/src/main/java/com/yahoo/schema/document/SDField.java