/*-------------------------------------------------------------------------*/ lpctype_t * get_array_type_with_depth (lpctype_t *element, int depth) /* Create an array whose depth is exactly <depth>. */ { lpctype_t *type; if (element->t_class == TCLASS_ARRAY) { if (element->t_array.depth == depth) return ref_lpctype(element); if (element->t_array.depth > depth) element = element->t_array.base; else depth -= element->t_array.depth; } type = ref_lpctype(element); for (; depth > 0; depth--) { lpctype_t *old = type; type = get_array_type(type); free_lpctype(old); } return type; } /* get_array_type_with_depth() */
DexType* get_concrete_type(SingleImpls& single_impls, DexType* type) { DexType* lookup_type = type; uint32_t array_level = get_array_level(type); if (array_level > 0) { auto array_type = get_array_type(type); assert(array_type); lookup_type = array_type; } const auto& intf_data = single_impls.find(lookup_type); if (intf_data != single_impls.end()) { auto concrete = intf_data->second.cls; if (array_level == 0) { return concrete; } const auto base_name = concrete->get_name()->c_str(); uint32_t size = array_level + strlen(base_name); char array_name[size]; char* p = array_name; while (array_level--) *p++ = '['; strcpy(p, concrete->get_name()->c_str()); auto array_type = DexType::get_type(array_name, size); return array_type; } return nullptr; }
Type Type::get_array_to() { type_t* result_type = this->_type_info; const decl_context_t* null_decl_context; memset(&null_decl_context, 0, sizeof(null_decl_context)); type_t* array_to = get_array_type(result_type, nodecl_null(), null_decl_context); return Type(array_to); }
Type Type::get_array_to(Nodecl::NodeclBase array_expr, Scope sc) { type_t* result_type = this->_type_info; const decl_context_t* decl_context = sc.get_decl_context(); type_t* array_to = get_array_type(result_type, array_expr.get_internal_nodecl(), decl_context); return Type(array_to); }
type_t* fortran_get_n_ranked_type(type_t* scalar_type, int rank, decl_context_t decl_context) { scalar_type = no_ref(scalar_type); ERROR_CONDITION(fortran_is_array_type(scalar_type), "This is not a scalar type!", 0); if (rank == 0) { return scalar_type; } else if (rank > 0) { return get_array_type(fortran_get_n_ranked_type(scalar_type, rank-1, decl_context), nodecl_null(), decl_context); } else { internal_error("Invalid rank %d\n", rank); } }
Expr* gather(Expr_seq const& subkeys) { // maintain the largest allowable key buffer uint512_t buf = 0; Evaluator ev; // maintain the position to start writing int pos = 0; for (auto subkey : subkeys) { // get the precision of the subkey int prec = precision(subkey->type()); Value const& val = ev.eval(subkey); // FIXME: for now we're only dealing with unsigned integer values assert(val.is_integer()); std::stringstream ss; ss << val.get_integer().decimal_str(); uint512_t i = 0; ss >> i; // shift the integer over by the amount already written i = i << pos; // add the length of the current integer to the pos pos += prec; // log-and the integer into the buffer buf |= i; } char* bytes = new char[pos / 8]; char* key = reinterpret_cast<char*>(&buf); std::copy(key, key + (pos / 8), bytes); Array_value arr { bytes, (size_t) pos / 8 }; Type const* z = get_integer_type(); Expr* n = new Literal_expr(z, arr.len + 1); // Create the array type. Type const* c = get_character_type(); Type const* t = get_array_type(c, n); return new Literal_expr(t, arr); }
// Build a new string literal. String literals // are arrays of characters. Expr* Parser::on_str(Token tok) { // Build the string value. String_sym const* s = tok.string_symbol(); Array_value v { s->value().c_str(), s->value().size() }; // Create the extent of the literal array. This is // explicitly more than the length of the string, // and includes the null character. Type const* z = get_integer_type(); Expr* n = new Literal_expr(z, v.len + 1); // Create the array type. Type const* c = get_character_type(); Type const* t = get_array_type(c, n); return init<Literal_expr>(tok.location(), t, v); }
/* * moveArrayTypeName * - try to reassign an array type name that the user wants to use. * * The given type name has been discovered to already exist (with the given * OID). If it is an autogenerated array type, change the array type's name * to not conflict. This allows the user to create type "foo" followed by * type "_foo" without problems. (Of course, there are race conditions if * two backends try to create similarly-named types concurrently, but the * worst that can happen is an unnecessary failure --- anything we do here * will be rolled back if the type creation fails due to conflicting names.) * * Note that this must be called *before* calling makeArrayTypeName to * determine the new type's own array type name; else the latter will * certainly pick the same name. * * Returns TRUE if successfully moved the type, FALSE if not. * * We also return TRUE if the given type is a shell type. In this case * the type has not been renamed out of the way, but nonetheless it can * be expected that TypeCreate will succeed. This behavior is convenient * for most callers --- those that need to distinguish the shell-type case * must do their own typisdefined test. */ bool moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace) { Oid elemOid; char *newname; /* We need do nothing if it's a shell type. */ if (!get_typisdefined(typeOid)) return true; /* Can't change it if it's not an autogenerated array type. */ elemOid = get_element_type(typeOid); if (!OidIsValid(elemOid) || get_array_type(elemOid) != typeOid) return false; /* * OK, use makeArrayTypeName to pick an unused modification of the name. * Note that since makeArrayTypeName is an iterative process, this will * produce a name that it might have produced the first time, had the * conflicting type we are about to create already existed. */ newname = makeArrayTypeName(typeName, typeNamespace); /* Apply the rename */ RenameTypeInternal(typeOid, newname, typeNamespace); /* * We must bump the command counter so that any subsequent use of * makeArrayTypeName sees what we just did and doesn't pick the same name. */ CommandCounterIncrement(); pfree(newname); return true; }
/* * make_scalar_array_op() * Build expression tree for "scalar op ANY/ALL (array)" construct. */ Expr * make_scalar_array_op(ParseState *pstate, List *opname, bool useOr, Node *ltree, Node *rtree, int location) { Oid ltypeId, rtypeId, atypeId, res_atypeId; Operator tup; Form_pg_operator opform; Oid actual_arg_types[2]; Oid declared_arg_types[2]; List *args; Oid rettype; ScalarArrayOpExpr *result; ltypeId = exprType(ltree); atypeId = exprType(rtree); /* * The right-hand input of the operator will be the element type of the * array. However, if we currently have just an untyped literal on the * right, stay with that and hope we can resolve the operator. */ if (atypeId == UNKNOWNOID) rtypeId = UNKNOWNOID; else { rtypeId = get_base_element_type(atypeId); if (!OidIsValid(rtypeId)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("op ANY/ALL (array) requires array on right side"), parser_errposition(pstate, location))); } /* Now resolve the operator */ tup = oper(pstate, opname, ltypeId, rtypeId, false, location); opform = (Form_pg_operator) GETSTRUCT(tup); /* Check it's not a shell */ if (!RegProcedureIsValid(opform->oprcode)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("operator is only a shell: %s", op_signature_string(opname, opform->oprkind, opform->oprleft, opform->oprright)), parser_errposition(pstate, location))); args = list_make2(ltree, rtree); actual_arg_types[0] = ltypeId; actual_arg_types[1] = rtypeId; declared_arg_types[0] = opform->oprleft; declared_arg_types[1] = opform->oprright; /* * enforce consistency with polymorphic argument and return types, * possibly adjusting return type or declared_arg_types (which will be * used as the cast destination by make_fn_arguments) */ rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, 2, opform->oprresult, false); /* * Check that operator result is boolean */ if (rettype != BOOLOID) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("op ANY/ALL (array) requires operator to yield boolean"), parser_errposition(pstate, location))); if (get_func_retset(opform->oprcode)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("op ANY/ALL (array) requires operator not to return a set"), parser_errposition(pstate, location))); /* * Now switch back to the array type on the right, arranging for any * needed cast to be applied. Beware of polymorphic operators here; * enforce_generic_type_consistency may or may not have replaced a * polymorphic type with a real one. */ if (IsPolymorphicType(declared_arg_types[1])) { /* assume the actual array type is OK */ res_atypeId = atypeId; } else { res_atypeId = get_array_type(declared_arg_types[1]); if (!OidIsValid(res_atypeId)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find array type for data type %s", format_type_be(declared_arg_types[1])), parser_errposition(pstate, location))); } actual_arg_types[1] = atypeId; declared_arg_types[1] = res_atypeId; /* perform the necessary typecasting of arguments */ make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); /* and build the expression node */ result = makeNode(ScalarArrayOpExpr); result->opno = oprid(tup); result->opfuncid = opform->oprcode; result->useOr = useOr; /* inputcollid will be set by parse_collate.c */ result->args = args; result->location = location; ReleaseSysCache(tup); return (Expr *) result; }
Type const* Parser::on_array_type(Type const* t , Expr* n) { return get_array_type(t, n); }
Datum xmlelement(PG_FUNCTION_ARGS) { Datum nameText; ArrayType *attrs = NULL; char *elName; unsigned int nameLen, resSizeMax; unsigned int childSize = 0; char *c, *result, *resData, *resCursor, *nameDst; XMLCompNodeHdr element; XMLNodeOffset *rootOffPtr; bool nameFirstChar = true; char **attrNames = NULL; char **attrValues = NULL; char *attrValFlags = NULL; XMLNodeHdr *attrNodes = NULL; XMLNodeHdr child = NULL; char **newNds = NULL; char *newNd = NULL; unsigned int attrCount = 0; unsigned int attrsSizeTotal = 0; unsigned short childCount = 0; if (PG_ARGISNULL(0)) { elog(ERROR, "invalid element name"); } nameText = PG_GETARG_DATUM(0); elName = TextDatumGetCString(nameText); nameLen = strlen(elName); if (nameLen == 0) { elog(ERROR, "invalid element name"); } if (!PG_ARGISNULL(1)) { int *dims; Oid elType, arrType; int16 arrLen, elLen; bool elByVal, elIsNull; char elAlign; unsigned int i; attrs = PG_GETARG_ARRAYTYPE_P(1); if (ARR_NDIM(attrs) != 2) { elog(ERROR, "attributes must be passed in 2 dimensional array"); } dims = ARR_DIMS(attrs); if (dims[1] != 2) { elog(ERROR, "the second dimension of attribute array must be 2"); } attrCount = dims[0]; Assert(attrCount > 0); elType = attrs->elemtype; arrType = get_array_type(elType); arrLen = get_typlen(arrType); Assert(arrType != InvalidOid); get_typlenbyvalalign(elType, &elLen, &elByVal, &elAlign); attrNames = (char **) palloc(attrCount * sizeof(char *)); attrValues = (char **) palloc(attrCount * sizeof(char *)); attrValFlags = (bool *) palloc(attrCount * sizeof(char)); for (i = 1; i <= attrCount; i++) { int subscrName[] = {i, 1}; int subscrValue[] = {i, 2}; Datum elDatum; char *nameStr, *valueStr; bool valueHasRefs = false; elDatum = array_ref(attrs, 2, subscrName, arrLen, elLen, elByVal, elAlign, &elIsNull); if (elIsNull) { elog(ERROR, "attribute name must not be null"); } nameStr = text_to_cstring(DatumGetTextP(elDatum)); if (strlen(nameStr) == 0) { elog(ERROR, "attribute name must be a string of non-zero length"); } else { /* Check validity of characters. */ char *c = nameStr; int cWidth = pg_utf_mblen((unsigned char *) c); if (!XNODE_VALID_NAME_START(c)) { elog(ERROR, "attribute name starts with invalid character"); } do { c += cWidth; cWidth = pg_utf_mblen((unsigned char *) c); } while (XNODE_VALID_NAME_CHAR(c)); if (*c != '\0') { elog(ERROR, "invalid character in attribute name"); } } /* Check uniqueness of the attribute name. */ if (i > 1) { unsigned short j; for (j = 0; j < (i - 1); j++) { if (strcmp(nameStr, attrNames[j]) == 0) { elog(ERROR, "attribute name '%s' is not unique", nameStr); } } } elDatum = array_ref(attrs, 2, subscrValue, arrLen, elLen, elByVal, elAlign, &elIsNull); if (elIsNull) { elog(ERROR, "attribute value must not be null"); } valueStr = text_to_cstring(DatumGetTextP(elDatum)); attrValFlags[i - 1] = 0; if (strlen(valueStr) > 0) { XMLNodeParserStateData state; char *valueStrOrig = valueStr; /* Parse the value and check validity. */ initXMLParserState(&state, valueStr, true); valueStr = readXMLAttValue(&state, true, &valueHasRefs); /* * If the value contains quotation mark, then apostrophe is * the delimiter. */ if (strchr(valueStr, XNODE_CHAR_QUOTMARK) != NULL) { attrValFlags[i - 1] |= XNODE_ATTR_APOSTROPHE; } finalizeXMLParserState(&state); pfree(valueStrOrig); } attrNames[i - 1] = nameStr; attrValues[i - 1] = valueStr; if (valueHasRefs) { attrValFlags[i - 1] |= XNODE_ATTR_CONTAINS_REF; } attrsSizeTotal += sizeof(XMLNodeHdrData) + strlen(nameStr) + strlen(valueStr) + 2; } } if (!PG_ARGISNULL(2)) { Datum childNodeDatum = PG_GETARG_DATUM(2); xmlnode childRaw = (xmlnode) PG_DETOAST_DATUM(childNodeDatum); child = XNODE_ROOT(childRaw); if (child->kind == XMLNODE_DOC_FRAGMENT) { childSize = getXMLNodeSize(child, true) - getXMLNodeSize(child, false); } else { childSize = getXMLNodeSize(child, true); } } /* Make sure the element name is valid. */ c = elName; while (*c != '\0') { if ((nameFirstChar && !XNODE_VALID_NAME_START(c)) || (!nameFirstChar && !XNODE_VALID_NAME_CHAR(c))) { elog(ERROR, "unrecognized character '%c' in element name", *c); } if (nameFirstChar) { nameFirstChar = false; } c += pg_utf_mblen((unsigned char *) c); }; if (child != NULL) { if (child->kind == XMLNODE_DOC_FRAGMENT) { childCount = ((XMLCompNodeHdr) child)->children; } else { childCount = 1; } } /* * It's hard to determine the byte width of references until the copying * has finished. Therefore we assume the worst case: 4 bytes per * reference. */ resSizeMax = VARHDRSZ + attrsSizeTotal + childSize + (attrCount + childCount) * 4 + sizeof(XMLCompNodeHdrData) + nameLen + 1 + sizeof(XMLNodeOffset); result = (char *) palloc(resSizeMax); resCursor = resData = VARDATA(result); if (attrCount > 0) { /* Copy attributes. */ unsigned short i; Assert(attrNames != NULL && attrValues != NULL && attrValFlags != NULL); attrNodes = (XMLNodeHdr *) palloc(attrCount * sizeof(XMLNodeHdr)); for (i = 0; i < attrCount; i++) { XMLNodeHdr attrNode = (XMLNodeHdr) resCursor; char *name = attrNames[i]; unsigned int nameLen = strlen(name); char *value = attrValues[i]; unsigned int valueLen = strlen(value); attrNodes[i] = attrNode; attrNode->kind = XMLNODE_ATTRIBUTE; attrNode->flags = attrValFlags[i]; if (xmlAttrValueIsNumber(value)) { attrNode->flags |= XNODE_ATTR_NUMBER; } resCursor = XNODE_CONTENT(attrNode); memcpy(resCursor, name, nameLen); resCursor += nameLen; *(resCursor++) = '\0'; pfree(name); memcpy(resCursor, value, valueLen); resCursor += valueLen; *(resCursor++) = '\0'; pfree(value); } pfree(attrNames); pfree(attrValues); pfree(attrValFlags); } if (child != NULL) { XMLNodeKind k = child->kind; /* * Check if the node to be inserted is of a valid kind. If the node is * document fragment, its assumed that invalid node kinds are never * added. Otherwise we'd have to check the node fragment (recursively) * not only here. */ if (k != XMLNODE_DOC_FRAGMENT) { if (k == XMLNODE_DOC || k == XMLNODE_DTD || k == XMLNODE_ATTRIBUTE) { elog(ERROR, "the nested node must not be %s", getXMLNodeKindStr(k)); } } copyXMLNodeOrDocFragment(child, childSize, &resCursor, &newNd, &newNds); } element = (XMLCompNodeHdr) resCursor; element->common.kind = XMLNODE_ELEMENT; element->common.flags = (child == NULL) ? XNODE_EMPTY : 0; element->children = attrCount + childCount; if (childCount > 0 || attrCount > 0) { XMLNodeOffset childOff, childOffMax; char bwidth; char *refPtr; /* Save relative offset(s) of the child node(s). */ if (attrCount > 0) { childOffMax = (char *) element - resData; } else if (childCount > 0) { if (child->kind == XMLNODE_DOC_FRAGMENT) { Assert(newNds != NULL); childOffMax = (char *) element - newNds[0]; } else { childOffMax = (char *) element - newNd; } } else { childOffMax = 0; } bwidth = getXMLNodeOffsetByteWidth(childOffMax); XNODE_SET_REF_BWIDTH(element, bwidth); refPtr = XNODE_FIRST_REF(element); if (attrCount > 0) { unsigned short i; /* The attribute references first... */ for (i = 0; i < attrCount; i++) { XMLNodeHdr node = attrNodes[i]; childOff = (char *) element - (char *) node; writeXMLNodeOffset(childOff, &refPtr, bwidth, true); } pfree(attrNodes); } if (childCount > 0) { /* ...followed by those of the other children. */ if (child->kind == XMLNODE_DOC_FRAGMENT) { unsigned short i; for (i = 0; i < childCount; i++) { childOff = (char *) element - newNds[i]; writeXMLNodeOffset(childOff, &refPtr, bwidth, true); } pfree(newNds); } else { childOff = (char *) element - newNd; writeXMLNodeOffset(childOff, &refPtr, bwidth, true); } } } /* And finally set the element name. */ nameDst = XNODE_ELEMENT_NAME(element); memcpy(nameDst, elName, nameLen); nameDst[nameLen] = '\0'; resCursor = nameDst + strlen(elName) + 1; SET_VARSIZE(result, (char *) resCursor - result + sizeof(XMLNodeOffset)); rootOffPtr = XNODE_ROOT_OFFSET_PTR(result); *rootOffPtr = (char *) element - resData; PG_RETURN_POINTER(result); }
const DexType* get_array_type_or_self(const DexType* type) { if (is_array(type)) { return get_array_type(type); } return type; }
/* * make_scalar_array_op() * Build expression tree for "scalar op ANY/ALL (array)" construct. */ Expr * make_scalar_array_op(ParseState *pstate, List *opname, bool useOr, Node *ltree, Node *rtree, int location) { Oid ltypeId, rtypeId, atypeId, res_atypeId; Operator tup; Form_pg_operator opform; Oid actual_arg_types[2]; Oid declared_arg_types[2]; List *args; Oid rettype; ScalarArrayOpExpr *result; ltypeId = exprType(ltree); atypeId = exprType(rtree); /* * The right-hand input of the operator will be the element type of the * array. However, if we currently have just an untyped literal on the * right, stay with that and hope we can resolve the operator. */ if (atypeId == UNKNOWNOID) rtypeId = UNKNOWNOID; else { rtypeId = get_element_type(atypeId); if (!OidIsValid(rtypeId)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("op ANY/ALL (array) requires array on right side"), errOmitLocation(true), parser_errposition(pstate, location))); } /* Now resolve the operator */ tup = oper(pstate, opname, ltypeId, rtypeId, false, location); opform = (Form_pg_operator) GETSTRUCT(tup); args = list_make2(ltree, rtree); actual_arg_types[0] = ltypeId; actual_arg_types[1] = rtypeId; declared_arg_types[0] = opform->oprleft; declared_arg_types[1] = opform->oprright; /* * enforce consistency with ANYARRAY and ANYELEMENT argument and return * types, possibly adjusting return type or declared_arg_types (which will * be used as the cast destination by make_fn_arguments) */ rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, 2, opform->oprresult); /* * Check that operator result is boolean */ if (rettype != BOOLOID) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("op ANY/ALL (array) requires operator to yield boolean"), errOmitLocation(true), parser_errposition(pstate, location))); if (get_func_retset(opform->oprcode)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("op ANY/ALL (array) requires operator not to return a set"), errOmitLocation(true), parser_errposition(pstate, location))); /* * Now switch back to the array type on the right, arranging for any * needed cast to be applied. */ res_atypeId = get_array_type(declared_arg_types[1]); if (!OidIsValid(res_atypeId)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find array type for data type %s", format_type_be(declared_arg_types[1])), errOmitLocation(true), parser_errposition(pstate, location))); actual_arg_types[1] = atypeId; declared_arg_types[1] = res_atypeId; /* perform the necessary typecasting of arguments */ make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); /* and build the expression node */ result = makeNode(ScalarArrayOpExpr); result->opno = oprid(tup); result->opfuncid = InvalidOid; result->useOr = useOr; result->args = args; ReleaseOperator(tup); return (Expr *) result; }
/*-------------------------------------------------------------------------*/ static lpctype_t * internal_get_common_type(lpctype_t *t1, lpctype_t* t2, bool find_one) /* Determine the intersection of both types. * Returns NULL if there is no common type. * If one of both types is TYPE_UNKNOWN, then * the result will by TYPE_UNKNOWN, too. * * If <find_one> is true, then it may finish even if only a part * of the result type was found (used for has_common_type()). */ { /* Hopefully the most common case. */ if (t1 && t1 == t2) return ref_lpctype(t1); /* We can't return NULL, as this is an error condition. */ if (t1 == NULL && t2 == NULL) return lpctype_mixed; else if (t1 == NULL) return ref_lpctype(t2); else if (t2 == NULL) return ref_lpctype(t1); if (t2->t_class == TCLASS_UNION && t1->t_class != TCLASS_UNION) { /* Switch them, so t2 is not a union unless t1 is one, too. */ lpctype_t *temp; temp = t1; t1 = t2; t2 = temp; } /* Some shortcuts, before we're diving into t1.*/ if (t2->t_class == TCLASS_PRIMARY) { switch (t2->t_primary) { case TYPE_UNKNOWN: return ref_lpctype(t2); case TYPE_ANY: return ref_lpctype(t1); default: break; } } switch (t1->t_class) { case TCLASS_PRIMARY: switch (t1->t_primary) { case TYPE_UNKNOWN: return ref_lpctype(t1); case TYPE_ANY: return ref_lpctype(t2); default: /* Primary types besides the above exceptions should be identical (checked at the beginning of this function). */ return NULL; } case TCLASS_STRUCT: if (t2->t_class != TCLASS_STRUCT) return NULL; else if (t1->t_struct == NULL) return ref_lpctype(t2); else if (t2->t_struct == NULL) return ref_lpctype(t1); /* This is somewhat counterintuitive, but the derived struct is more specialized, so it is the result of the intersection. */ else if (struct_baseof(t1->t_struct, t2->t_struct)) return ref_lpctype(t2); else if (struct_baseof(t2->t_struct, t1->t_struct)) return ref_lpctype(t1); else return NULL; case TCLASS_ARRAY: if (t2->t_class != TCLASS_ARRAY) return NULL; else { lpctype_t *common_element = get_common_type(t1->t_array.element, t2->t_array.element); lpctype_t *result = get_array_type(common_element); free_lpctype(common_element); return result; } case TCLASS_UNION: { lpctype_t *result = NULL; while (true) { lpctype_t *base = t1->t_class == TCLASS_UNION ? t1->t_union.member : t1; lpctype_t *common_base = get_common_type(t2, base); lpctype_t *oldresult = result; if (find_one && common_base) return common_base; result = get_union_type(result, common_base); free_lpctype(common_base); free_lpctype(oldresult); if (t1->t_class == TCLASS_UNION) t1 = t1->t_union.head; else break; } return result; } default: fatal("Unknown type class %d!\n", t1->t_class); return NULL; } } /* internal_get_common_type() */