From b802efde5446433af827dd46241dc7cf18b6a83b Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 15 Mar 2021 16:59:08 +0800 Subject: [PATCH] Fixed ternary result type detection with pointer types. Fixed pointer type comparison and conversion, especially regarding qualifiers. Improved diagnostics about type comparison and conversion. Reorganized some type-comparison/conversion functions. --- src/cc65/assignment.c | 2 +- src/cc65/datatype.h | 1 + src/cc65/declare.c | 2 +- src/cc65/expr.c | 78 +++++--- src/cc65/symtab.c | 22 ++- src/cc65/typecmp.c | 403 +++++++++++++++++++----------------------- src/cc65/typecmp.h | 48 +++-- src/cc65/typeconv.c | 235 +++++++++++++++++------- src/cc65/typeconv.h | 11 +- 9 files changed, 466 insertions(+), 336 deletions(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 737d059a0..c642b4ce9 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -78,7 +78,7 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) hie1 (RExpr); /* Check for equality of the structs/unions */ - if (TypeCmp (ltype, RExpr->Type) < TC_STRICT_COMPATIBLE) { + if (TypeCmp (ltype, RExpr->Type).C < TC_STRICT_COMPATIBLE) { TypeCompatibilityDiagnostic (ltype, RExpr->Type, 1, "Incompatible types in assignment to '%s' from '%s'"); } diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index ac6edeeb5..e6ff9b7ce 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -108,6 +108,7 @@ enum { T_QUAL_CONST = 0x001000, T_QUAL_VOLATILE = 0x002000, T_QUAL_RESTRICT = 0x004000, + T_QUAL_CVR = T_QUAL_CONST | T_QUAL_VOLATILE | T_QUAL_RESTRICT, T_QUAL_NEAR = 0x008000, T_QUAL_FAR = 0x010000, T_QUAL_ADDRSIZE = T_QUAL_NEAR | T_QUAL_FAR, diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 787ed62b7..0c00f56d4 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1821,7 +1821,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) NextToken (); /* Allow const, restrict, and volatile qualifiers */ - Qualifiers |= OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE | T_QUAL_RESTRICT); + Qualifiers |= OptionalQualifiers (T_QUAL_CVR); /* Parse the type that the pointer points to */ Declarator (Spec, D, Mode); diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 0bb393cf1..be53b22f9 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -2447,15 +2447,11 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ } } else if (IsClassPtr (Expr->Type)) { if (IsClassPtr (Expr2.Type)) { - /* Both pointers are allowed in comparison if they point to - ** the same type, or if one of them is a void pointer. - */ - Type* left = Indirect (Expr->Type); - Type* right = Indirect (Expr2.Type); - if (TypeCmp (left, right) < TC_QUAL_DIFF && left->C != T_VOID && right->C != T_VOID) { - /* Incompatible pointers */ + /* Pointers are allowed in comparison */ + if (TypeCmp (Expr->Type, Expr2.Type).C < TC_STRICT_COMPATIBLE) { + /* Warn about distinct pointer types */ TypeCompatibilityDiagnostic (PtrConversion (Expr->Type), PtrConversion (Expr2.Type), 0, - "Incompatible pointer types comparing '%s' with '%s'"); + "Distinct pointer types comparing '%s' with '%s'"); } } else if (!ED_IsNullPtr (&Expr2)) { if (IsClassInt (Expr2.Type)) { @@ -3268,17 +3264,25 @@ static void parsesub (ExprDesc* Expr) if (IsClassPtr (lhst) && IsClassPtr (rhst)) { /* Pointer diff */ - if (TypeCmp (Indirect (lhst), Indirect (rhst)) < TC_QUAL_DIFF) { - Error ("Incompatible pointer types"); + if (TypeCmp (lhst, rhst).C >= TC_STRICT_COMPATIBLE) { + /* We'll have to scale the result */ + rscale = PSizeOf (lhst); + /* We cannot scale by 0-size or unknown-size */ + if (rscale == 0) { + TypeCompatibilityDiagnostic (lhst, rhst, + 1, "Invalid pointer types in subtraction: '%s' and '%s'"); + /* Avoid further errors */ + rscale = 1; + } + } else { + TypeCompatibilityDiagnostic (lhst, rhst, + 1, "Incompatible pointer types in subtraction: '%s' and '%s'"); } /* Operate on pointers, result type is an integer */ flags = CF_PTR; Expr->Type = type_int; - /* We'll have to scale the result */ - rscale = CheckedPSizeOf (lhst); - /* Check for a constant rhs expression */ if (ED_IsQuasiConst (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { /* The right hand side is constant. Check left hand side. */ @@ -4069,13 +4073,17 @@ static void hieQuest (ExprDesc* Expr) ** Conversion rules for ?: expression are: ** - if both expressions are int expressions, default promotion ** rules for ints apply. - ** - if both expressions are pointers of the same type, the - ** result of the expression is of this type. + ** - if both expressions have the same structure, union or void type, + ** the result has the same type. + ** - if both expressions are pointers to compatible types (possibly + ** qualified differently), the result of the expression is an + ** appropriately qualified version of the composite type. + ** - if one of the expressions is a pointer and the other is a + ** pointer to (possibly qualified) void, the resulting type is a + ** pointer to appropriately qualified void. ** - if one of the expressions is a pointer and the other is - ** a zero constant, the resulting type is that of the pointer - ** type. - ** - if both expressions are void expressions, the result is of - ** type void. + ** a null pointer constant, the resulting type is that of the + ** pointer type. ** - all other cases are flagged by an error. */ if (IsClassInt (Expr2.Type) && IsClassInt (Expr3.Type)) { @@ -4105,12 +4113,28 @@ static void hieQuest (ExprDesc* Expr) } } else if (IsClassPtr (Expr2.Type) && IsClassPtr (Expr3.Type)) { - /* Must point to same type */ - if (TypeCmp (Indirect (Expr2.Type), Indirect (Expr3.Type)) < TC_EQUAL) { - Error ("Incompatible pointer types"); + /* If one of the two is 'void *', the result type is a pointer to + ** appropriately qualified void. + */ + if (IsTypeVoid (Indirect (Expr2.Type))) { + ResultType = PointerTo (Indirect (Expr2.Type)); + ResultType[1].C |= GetQualifier (Indirect (Expr3.Type)); + } else if (IsTypeVoid (Indirect (Expr3.Type))) { + ResultType = PointerTo (Indirect (Expr3.Type)); + ResultType[1].C |= GetQualifier (Indirect (Expr2.Type)); + } else { + /* Must point to compatible types */ + if (TypeCmp (Expr2.Type, Expr3.Type).C < TC_VOID_PTR) { + TypeCompatibilityDiagnostic (Expr2.Type, Expr3.Type, + 1, "Incompatible pointer types in ternary: '%s' and '%s'"); + /* Avoid further errors */ + ResultType = PointerTo (type_void); + } else { + /* Result has the composite type */ + ResultType = TypeDup (Expr2.Type); + TypeComposition (ResultType, Expr3.Type); + } } - /* Result has the common type */ - ResultType = Expr2.Type; } else if (IsClassPtr (Expr2.Type) && Expr3IsNULL) { /* Result type is pointer, no cast needed */ ResultType = Expr2.Type; @@ -4119,10 +4143,10 @@ static void hieQuest (ExprDesc* Expr) ResultType = Expr3.Type; } else if (IsTypeVoid (Expr2.Type) && IsTypeVoid (Expr3.Type)) { /* Result type is void */ - ResultType = Expr3.Type; + ResultType = type_void; } else { if (IsClassStruct (Expr2.Type) && IsClassStruct (Expr3.Type) && - TypeCmp (Expr2.Type, Expr3.Type) == TC_IDENTICAL) { + TypeCmp (Expr2.Type, Expr3.Type).C == TC_IDENTICAL) { /* Result type is struct/union */ ResultType = Expr2.Type; } else { @@ -4148,7 +4172,7 @@ static void hieQuest (ExprDesc* Expr) } /* Setup the target expression */ - Expr->Type = ResultType; + Expr->Type = ResultType; } } diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 3d7176a83..6ec255497 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -59,6 +59,7 @@ #include "stackptr.h" #include "symentry.h" #include "typecmp.h" +#include "typeconv.h" #include "symtab.h" @@ -549,6 +550,19 @@ SymEntry FindStructField (const Type* T, const char* Name) +static int IsDistinctRedef (const Type* lhst, const Type* rhst, typecmpcode_t Code, typecmpflag_t Flags) +/* Return if type compatibility result is "worse" than Code or if any bit of +** qualifier Flags is set. +*/ +{ + typecmp_t Result = TypeCmp (lhst, rhst); + if (Result.C < Code || (Result.F & TCF_MASK_QUAL & Flags) != 0) { + return 1; + } + return 0; +} + + static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags) /* Check and handle redefinition of existing symbols. ** Complete array sizes and function descriptors as well. @@ -565,7 +579,7 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags /* Existing typedefs cannot be redeclared as anything different */ if (SCType == SC_TYPEDEF) { - if (TypeCmp (E_Type, T) < TC_IDENTICAL) { + if (IsDistinctRedef (E_Type, T, TC_IDENTICAL, TCF_MASK_QUAL)) { Error ("Conflicting types for typedef '%s'", Entry->Name); Entry = 0; } @@ -591,7 +605,7 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags Entry = 0; } else { /* New type must be compatible with the composite prototype */ - if (TypeCmp (Entry->Type, T) < TC_EQUAL) { + if (IsDistinctRedef (E_Type, T, TC_EQUAL, TCF_MASK_QUAL)) { Error ("Conflicting function types for '%s'", Entry->Name); Entry = 0; } else { @@ -621,7 +635,7 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags ** is incomplete, complete it. */ if ((Size != UNSPECIFIED && ESize != UNSPECIFIED && Size != ESize) || - TypeCmp (T + 1, E_Type + 1) < TC_EQUAL) { + IsDistinctRedef (E_Type + 1, T + 1, TC_IDENTICAL, TCF_MASK_QUAL)) { /* Conflicting element types */ Error ("Conflicting array types for '%s[]'", Entry->Name); Entry = 0; @@ -639,7 +653,7 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags if (SCType != E_SCType) { Error ("Redefinition of '%s' as different kind of symbol", Entry->Name); Entry = 0; - } else if (TypeCmp (E_Type, T) < TC_EQUAL) { + } else if (IsDistinctRedef (E_Type, T, TC_EQUAL, TCF_MASK_QUAL)) { Error ("Conflicting types for '%s'", Entry->Name); Entry = 0; } else if (E_SCType == SC_ENUMERATOR) { diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index f204b9a0b..7aae7fefe 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -36,7 +36,7 @@ #include /* cc65 */ -#include "funcdesc.h" +#include "error.h" #include "global.h" #include "symtab.h" #include "typecmp.h" @@ -49,17 +49,6 @@ -static void SetResult (typecmp_t* Result, typecmp_t Val) -/* Set a new result value if it is less than the existing one */ -{ - if (Val < *Result) { - /* printf ("SetResult = %d\n", Val); */ - *Result = Val; - } -} - - - static int ParamsHaveDefaultPromotions (const FuncDesc* F) /* Check if any of the parameters of function F has a default promotion. In ** this case, the function is not compatible with an empty parameter name list @@ -129,7 +118,7 @@ static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2) } /* Compare this field */ - if (TypeCmp (Type1, Type2) < TC_EQUAL) { + if (TypeCmp (Type1, Type2).C < TC_EQUAL) { /* Field types not equal */ return 0; } @@ -148,48 +137,169 @@ static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2) +static void SetResult (typecmp_t* Result, typecmpcode_t Val) +/* Set a new result value if it is less than the existing one */ +{ + if (Val < Result->C) { + if (Result->Indirections > 0) { + if (Val >= TC_STRICT_COMPATIBLE) { + Result->C = Val; + } else if (Result->Indirections == 1) { + /* C Standard allows implicit conversion as long as one side is + ** a pointer to void type, but doesn't care which side is. + */ + if ((Result->F & TCF_MASK_VOID_PTR) != 0) { + Result->C = TC_VOID_PTR; + } else { + Result->C = TC_PTR_INCOMPATIBLE; + } + } else { + Result->C = TC_PTR_INCOMPATIBLE; + } + } else { + Result->C = Val; + } + /* printf ("SetResult = %d\n", Val); */ + } +} + + + +static typecmp_t* CmpQuals (const Type* lhst, const Type* rhst, typecmp_t* Result) +/* Copare the types regarding thier qualifiers. Return the Result */ +{ + TypeCode LeftQual, RightQual; + + /* Get the left and right qualifiers */ + LeftQual = GetQualifier (lhst); + RightQual = GetQualifier (rhst); + + /* If type is function without a calling convention set explicitly, + ** then assume the default one. + */ + if (IsTypeFunc (lhst)) { + if ((LeftQual & T_QUAL_CCONV) == T_QUAL_NONE) { + LeftQual |= (AutoCDecl || IsVariadicFunc (lhst)) ? T_QUAL_CDECL : T_QUAL_FASTCALL; + } + } + if (IsTypeFunc (rhst)) { + if ((RightQual & T_QUAL_CCONV) == T_QUAL_NONE) { + RightQual |= (AutoCDecl || IsVariadicFunc (rhst)) ? T_QUAL_CDECL : T_QUAL_FASTCALL; + } + } + + /* Default address size qualifiers */ + if ((LeftQual & T_QUAL_ADDRSIZE) == T_QUAL_NONE) { + LeftQual |= (IsTypeFunc (lhst) ? CodeAddrSizeQualifier () : DataAddrSizeQualifier ()); + } + if ((RightQual & T_QUAL_ADDRSIZE) == T_QUAL_NONE) { + RightQual |= (IsTypeFunc (rhst) ? CodeAddrSizeQualifier () : DataAddrSizeQualifier ()); + } + + /* Just return if nothing to do */ + if (LeftQual == RightQual) { + return Result; + } + + /* On the first indirection level, different qualifiers mean that the types + ** are still compatible. On the second level, that is a (maybe minor) error. + ** We create a special return-code if a qualifier is dropped from a pointer. + ** But, different calling conventions are incompatible. Starting from the + ** next level, the types are incompatible if the qualifiers differ. + */ + /* (Debugging statement) */ + /* printf ("Ind = %d %06X != %06X\n", Result->Indirections, LeftQual, RightQual); */ + switch (Result->Indirections) { + case 0: + /* Compare C qualifiers */ + if ((LeftQual & T_QUAL_CVR) > (RightQual & T_QUAL_CVR)) { + Result->F |= TCF_QUAL_IMPLICIT; + } else if ((LeftQual & T_QUAL_CVR) != (RightQual & T_QUAL_CVR)) { + Result->F |= TCF_QUAL_DIFF; + } + + /* Compare address size qualifiers */ + if ((LeftQual & T_QUAL_ADDRSIZE) != (RightQual & T_QUAL_ADDRSIZE)) { + Result->F |= TCF_ADDRSIZE_QUAL_DIFF; + } + + /* Compare function calling conventions */ + if ((LeftQual & T_QUAL_CCONV) != (RightQual & T_QUAL_CCONV)) { + SetResult (Result, TC_INCOMPATIBLE); + } + break; + + case 1: + /* A non-const value on the right is compatible to a + ** const one to the left, same for volatile. + */ + if ((LeftQual & T_QUAL_CVR) > (RightQual & T_QUAL_CVR)) { + Result->F |= TCF_PTR_QUAL_IMPLICIT; + } else if ((LeftQual & T_QUAL_CVR) != (RightQual & T_QUAL_CVR)) { + Result->F |= TCF_PTR_QUAL_DIFF; + } + + /* Compare address size qualifiers */ + if ((LeftQual & T_QUAL_ADDRSIZE) != (RightQual & T_QUAL_ADDRSIZE)) { + Result->F |= TCF_ADDRSIZE_QUAL_DIFF; + } + + /* Compare function calling conventions */ + if ((!IsTypeFunc (lhst) && !IsTypeFunc (rhst)) || + (LeftQual & T_QUAL_CCONV) == (RightQual & T_QUAL_CCONV)) { + break; + } + /* else fall through */ + + default: + /* Pointer types mismatch */ + SetResult (Result, TC_INCOMPATIBLE); + break; + } + + return Result; +} + + + static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Recursively compare two types. */ { - unsigned Indirections; - unsigned ElementCount; SymEntry* Sym1; SymEntry* Sym2; FuncDesc* F1; FuncDesc* F2; + TypeCode LeftType, RightType; + long LeftCount, RightCount; - /* Initialize stuff */ - Indirections = 0; - ElementCount = 0; - /* Compare two types. Determine, where they differ */ while (lhs->C != T_END) { - TypeCode LeftType, RightType; - TypeCode LeftSign, RightSign; - TypeCode LeftQual, RightQual; - long LeftCount, RightCount; - /* Check if the end of the type string is reached */ if (rhs->C == T_END) { /* End of comparison reached */ + break; + } + + /* Compare qualifiers */ + if (CmpQuals (lhs, rhs, Result)->C == TC_INCOMPATIBLE) { return; } - /* Get the left and right types, signs and qualifiers */ + /* Get the left and right types */ LeftType = (GetUnderlyingTypeCode (lhs) & T_MASK_TYPE); RightType = (GetUnderlyingTypeCode (rhs) & T_MASK_TYPE); - LeftSign = GetSignedness (lhs); - RightSign = GetSignedness (rhs); - LeftQual = GetQualifier (lhs); - RightQual = GetQualifier (rhs); - /* If the left type is a pointer and the right is an array, both - ** are compatible. + /* If one side is a pointer and the other side is an array, both are + ** compatible. */ if (LeftType == T_TYPE_PTR && RightType == T_TYPE_ARRAY) { RightType = T_TYPE_PTR; + SetResult (Result, TC_PTR_DECAY); + } + if (LeftType == T_TYPE_ARRAY && RightType == T_TYPE_PTR) { + LeftType = T_TYPE_PTR; SetResult (Result, TC_STRICT_COMPATIBLE); } @@ -236,74 +346,30 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* 'char' is neither 'signed char' nor 'unsigned char' */ if ((IsISOChar (lhs) && !IsISOChar (rhs)) || (!IsISOChar (lhs) && IsISOChar (rhs))) { - SetResult (Result, TC_COMPATIBLE); + SetResult (Result, TC_SIGN_DIFF); } - /* On indirection level zero, a qualifier or sign difference is - ** accepted. The types are no longer equal, but compatible. + /* On indirection level zero, a sign difference is accepted. + ** The types are no longer equal, but compatible. */ - if (LeftSign != RightSign) { - if (ElementCount == 0) { - SetResult (Result, TC_SIGN_DIFF); - } else { - SetResult (Result, TC_INCOMPATIBLE); - return; - } - } - - if (LeftType == T_TYPE_FUNC) { - /* If a calling convention wasn't set explicitly, - ** then assume the default one. - */ - if ((LeftQual & T_QUAL_CCONV) == T_QUAL_NONE) { - LeftQual |= (AutoCDecl || IsVariadicFunc (lhs)) ? T_QUAL_CDECL : T_QUAL_FASTCALL; - } - if ((RightQual & T_QUAL_CCONV) == T_QUAL_NONE) { - RightQual |= (AutoCDecl || IsVariadicFunc (rhs)) ? T_QUAL_CDECL : T_QUAL_FASTCALL; - } - } - - if (LeftQual != RightQual) { - /* On the first indirection level, different qualifiers mean - ** that the types still are compatible. On the second level, - ** that is a (maybe minor) error. We create a special return-code - ** if a qualifier is dropped from a pointer. But, different calling - ** conventions are incompatible. Starting from the next level, - ** the types are incompatible if the qualifiers differ. - */ - /* (Debugging statement) */ - /* printf ("Ind = %d %06X != %06X\n", Indirections, LeftQual, RightQual); */ - switch (Indirections) { - case 0: - SetResult (Result, TC_STRICT_COMPATIBLE); - break; - - case 1: - /* A non-const value on the right is compatible to a - ** const one to the left, same for volatile. - */ - if ((LeftQual & T_QUAL_CONST) < (RightQual & T_QUAL_CONST) || - (LeftQual & T_QUAL_VOLATILE) < (RightQual & T_QUAL_VOLATILE)) { - SetResult (Result, TC_QUAL_DIFF); - } else { - SetResult (Result, TC_STRICT_COMPATIBLE); - } - - if (LeftType != T_TYPE_FUNC || (LeftQual & T_QUAL_CCONV) == (RightQual & T_QUAL_CCONV)) { - break; - } - /* else fall through */ - - default: - SetResult (Result, TC_INCOMPATIBLE); - return; - } + if (GetSignedness (lhs) != GetSignedness (rhs)) { + SetResult (Result, TC_SIGN_DIFF); } /* Check for special type elements */ switch (LeftType) { case T_TYPE_PTR: - ++Indirections; + ++Result->Indirections; + if (Result->Indirections == 1) { + if ((GetUnderlyingTypeCode (lhs + 1) & T_MASK_TYPE) == T_TYPE_VOID) { + Result->F |= TCF_VOID_PTR_ON_LEFT; + } + if ((GetUnderlyingTypeCode (rhs + 1) & T_MASK_TYPE) == T_TYPE_VOID) { + Result->F |= TCF_VOID_PTR_ON_RIGHT; + } + } else { + Result->F &= ~TCF_MASK_VOID_PTR; + } break; case T_TYPE_FUNC: @@ -364,7 +430,13 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) SetResult (Result, TC_INCOMPATIBLE); return; } - SetResult (Result, TC_EQUAL); + + /* We take into account which side is more specified */ + if (LeftCount == UNSPECIFIED) { + SetResult (Result, TC_UNSPECIFY); + } else { + SetResult (Result, TC_EQUAL); + } } break; @@ -397,11 +469,10 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Next type string element */ ++lhs; ++rhs; - ++ElementCount; } - /* Check if end of rhs reached */ - if (rhs->C == T_END) { + /* Check if lhs and rhs both reached ends */ + if (lhs->C == T_END && rhs->C == T_END) { SetResult (Result, TC_IDENTICAL); } else { SetResult (Result, TC_INCOMPATIBLE); @@ -414,7 +485,7 @@ typecmp_t TypeCmp (const Type* lhs, const Type* rhs) /* Compare two types and return the result */ { /* Assume the types are identical */ - typecmp_t Result = TC_IDENTICAL; + typecmp_t Result = TYPECMP_INITIALIZER; #if 0 printf ("Left : "); PrintRawType (stdout, lhs); @@ -432,134 +503,18 @@ typecmp_t TypeCmp (const Type* lhs, const Type* rhs) -static Type* DoComposite (Type* lhs, const Type* rhs); - -static void CompositeFuncParams (const FuncDesc* F1, const FuncDesc* F2) -/* Composite two function symbol tables regarding function parameters */ +void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg) +/* Print error or warning message about type compatibility with proper type names */ { - /* Get the symbol tables */ - const SymTable* Tab1 = F1->SymTab; - const SymTable* Tab2 = F2->SymTab; - - /* Composite the parameter lists */ - const SymEntry* Sym1 = Tab1->SymHead; - const SymEntry* Sym2 = Tab2->SymHead; - - /* Composite the fields */ - while (Sym1 && (Sym1->Flags & SC_PARAM) && Sym2 && (Sym2->Flags & SC_PARAM)) { - - /* Get the symbol types */ - Type* Type1 = Sym1->Type; - Type* Type2 = Sym2->Type; - - /* If either of both functions is old style, apply the default - ** promotions to the parameter type. - */ - if (F1->Flags & FD_OLDSTYLE) { - if (IsClassInt (Type1)) { - Type1 = IntPromotion (Type1); - } - } - if (F2->Flags & FD_OLDSTYLE) { - if (IsClassInt (Type2)) { - Type2 = IntPromotion (Type2); - } - } - - /* Composite this field */ - DoComposite (Type1, Type2); - - /* Get the pointers to the next fields */ - Sym1 = Sym1->NextSym; - Sym2 = Sym2->NextSym; + StrBuf NewTypeName = STATIC_STRBUF_INITIALIZER; + StrBuf OldTypeName = STATIC_STRBUF_INITIALIZER; + GetFullTypeNameBuf (&NewTypeName, NewType); + GetFullTypeNameBuf (&OldTypeName, OldType); + if (IsError) { + Error (Msg, SB_GetConstBuf (&NewTypeName), SB_GetConstBuf (&OldTypeName)); + } else { + Warning (Msg, SB_GetConstBuf (&NewTypeName), SB_GetConstBuf (&OldTypeName)); } -} - - - -static Type* DoComposite (Type* lhs, const Type* rhs) -/* Recursively composite two types into lhs */ -{ - FuncDesc* F1; - FuncDesc* F2; - long LeftCount, RightCount; - - /* Composite two types */ - while (lhs->C != T_END) { - - /* Check if the end of the type string is reached */ - if (rhs->C == T_END) { - return lhs; - } - - /* Check for sanity */ - CHECK (GetUnderlyingTypeCode (lhs) == GetUnderlyingTypeCode (rhs)); - - /* Check for special type elements */ - - if (IsTypeFunc (lhs)) { - /* Composite the function descriptors */ - F1 = GetFuncDesc (lhs); - F2 = GetFuncDesc (rhs); - - /* If one of both functions has an empty parameter list (which - ** does also mean, it is not a function definition, because the - ** flag is reset in this case), it is replaced by the other - ** definition, provided that the other has no default - ** promotions in the parameter list. If none of both parameter - ** lists is empty, we have to composite the parameter lists and - ** other attributes. - */ - if ((F1->Flags & FD_EMPTY) == FD_EMPTY) { - if ((F2->Flags & FD_EMPTY) == 0) { - /* Copy the parameters and flags */ - TypeCopy (lhs, rhs); - F1->Flags = F2->Flags; - } - } else if ((F2->Flags & FD_EMPTY) == 0) { - /* Composite the parameter lists */ - CompositeFuncParams (F1, F2); - } - - } else if (IsTypeArray (lhs)) { - /* Check member count */ - LeftCount = GetElementCount (lhs); - RightCount = GetElementCount (rhs); - - /* Set composite type if it is requested */ - if (LeftCount != UNSPECIFIED) { - SetElementCount (lhs, LeftCount); - } else if (RightCount != UNSPECIFIED) { - SetElementCount (lhs, RightCount); - } - } - - /* Next type string element */ - ++lhs; - ++rhs; - } - - return lhs; -} - - - -FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType) -/* Refine the existing function descriptor with a new one */ -{ - FuncDesc* Old = GetFuncDesc (OldType); - FuncDesc* New = GetFuncDesc (NewType); - - CHECK (Old != 0 && New != 0); - - if ((New->Flags & FD_EMPTY) == 0) { - if ((Old->Flags & FD_EMPTY) == 0) { - DoComposite (OldType, NewType); - } else { - TypeCopy (OldType, NewType); - Old->Flags &= ~FD_EMPTY; - } - } - - return Old; + SB_Done (&OldTypeName); + SB_Done (&NewTypeName); } diff --git a/src/cc65/typecmp.h b/src/cc65/typecmp.h index f4a87bcf6..e1ca699cf 100644 --- a/src/cc65/typecmp.h +++ b/src/cc65/typecmp.h @@ -48,18 +48,43 @@ -/* Degree of type compatibility. Must be in ascending order */ +/* Degree of type compatibility affected. Must be in ascending order */ typedef enum { - TC_INCOMPATIBLE, /* Distinct types */ - TC_SIGN_DIFF, /* Signedness differs */ - TC_COMPATIBLE = TC_SIGN_DIFF, /* Compatible types */ - TC_QUAL_DIFF, /* Types differ in qualifier of pointer */ - TC_STRICT_COMPATIBLE, /* Strict compatibility */ - TC_EQUAL, /* Types are equivalent */ - TC_IDENTICAL /* Types are identical */ + TC_INCOMPATIBLE, /* Distinct types */ + TC_SIGN_DIFF, /* Signedness differs */ + TC_PTR_INCOMPATIBLE, /* Distinct pointer types */ + TC_VOID_PTR, /* Non-void and void pointers */ + TC_STRICT_COMPATIBLE, /* Strict compatibility according to the C Standard */ + TC_PTR_DECAY, /* rhs is an array and lhs is a pointer */ + TC_EQUAL, /* Array types with unspecified lengths */ + TC_UNSPECIFY, /* lhs has unspecified length while rhs has specified length */ + TC_IDENTICAL /* Types are identical */ +} typecmpcode_t; + +/* Degree of type compatibility affected by qualifiers as well as some extra info */ +typedef enum { + TCF_NONE = 0x00, /* None of the below */ + TCF_VOID_PTR_ON_LEFT = 0x01, /* lhs is a void pointer */ + TCF_VOID_PTR_ON_RIGHT = 0x02, /* rhs is a void pointer */ + TCF_MASK_VOID_PTR = TCF_VOID_PTR_ON_LEFT | TCF_VOID_PTR_ON_RIGHT, + TCF_QUAL_DIFF = 0x04, /* CVR qualifiers differ in a way that doesn't matter */ + TCF_QUAL_IMPLICIT = 0x08, /* CVR qualifiers of lhs are stricter than those of rhs */ + TCF_PTR_QUAL_DIFF = 0x10, /* CVR qualifiers of pointers differ */ + TCF_PTR_QUAL_IMPLICIT = 0x20, /* CVR qualifiers of pointers are stricter on lhs than those on rhs */ + TCF_MASK_C_QUAL_DIFF = 0x3C, /* All C Standard qualifiers */ + TCF_ADDRSIZE_QUAL_DIFF = 0x40, /* Address size qualifiers differ */ + TCF_CCONV_QUAL_DIFF = 0x80, /* Function calling conventions differ. Unused now */ + TCF_INCOMPATIBLE_QUAL = TCF_ADDRSIZE_QUAL_DIFF | TCF_CCONV_QUAL_DIFF, + TCF_MASK_QUAL = TCF_MASK_C_QUAL_DIFF | TCF_INCOMPATIBLE_QUAL, +} typecmpflag_t; + +typedef struct { + typecmpcode_t C; + typecmpflag_t F; + int Indirections; } typecmp_t; - +#define TYPECMP_INITIALIZER { TC_IDENTICAL, TCF_NONE, 0 } /*****************************************************************************/ /* Code */ @@ -70,8 +95,9 @@ typedef enum { typecmp_t TypeCmp (const Type* lhs, const Type* rhs); /* Compare two types and return the result */ -FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType); -/* Refine the existing function descriptor with a new one */ +void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg); +/* Print error or warning message about type compatibility with proper type names */ + /* End of typecmp.h */ diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 930aef8d4..d75d13cd1 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -43,7 +43,6 @@ #include "error.h" #include "expr.h" #include "loadexpr.h" -#include "scanner.h" #include "typecmp.h" #include "typeconv.h" @@ -55,24 +54,6 @@ -void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg) -/* Print error or warning message about type conversion with proper type names */ -{ - StrBuf NewTypeName = STATIC_STRBUF_INITIALIZER; - StrBuf OldTypeName = STATIC_STRBUF_INITIALIZER; - GetFullTypeNameBuf (&NewTypeName, NewType); - GetFullTypeNameBuf (&OldTypeName, OldType); - if (IsError) { - Error (Msg, SB_GetConstBuf (&NewTypeName), SB_GetConstBuf (&OldTypeName)); - } else { - Warning (Msg, SB_GetConstBuf (&NewTypeName), SB_GetConstBuf (&OldTypeName)); - } - SB_Done (&OldTypeName); - SB_Done (&NewTypeName); -} - - - static void DoConversion (ExprDesc* Expr, const Type* NewType) /* Emit code to convert the given expression to a new type. */ { @@ -208,9 +189,10 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) PrintRawType (stdout, NewType); #endif /* First, do some type checking */ - int HasWarning = 0; - int HasError = 0; - const char* Msg = 0; + typecmp_t Result = TYPECMP_INITIALIZER; + int HasWarning = 0; + int HasError = 0; + const char* Msg = 0; const Type* OldType = Expr->Type; @@ -219,20 +201,13 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) HasError = 1; } - /* If both types are strictly compatible, no conversion is needed */ - if (TypeCmp (NewType, OldType) >= TC_STRICT_COMPATIBLE) { - /* We're already done */ - return; - } - - /* If Expr is an array or a function, convert it to a pointer */ - Expr->Type = PtrConversion (Expr->Type); - - /* If we have changed the type, check again for strictly compatibility */ - if (Expr->Type != OldType && - TypeCmp (NewType, Expr->Type) >= TC_STRICT_COMPATIBLE) { - /* We're already done */ - return; + /* If both types are the same, no conversion is needed */ + Result = TypeCmp (NewType, OldType); + if (Result.C < TC_IDENTICAL && (IsTypeArray (OldType) || IsTypeFunc (OldType))) { + /* If Expr is an array or a function, convert it to a pointer */ + Expr->Type = PtrConversion (Expr->Type); + /* Recompare */ + Result = TypeCmp (NewType, Expr->Type); } /* Check for conversion problems */ @@ -253,37 +228,27 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) /* Handle conversions to pointer type */ if (IsClassPtr (Expr->Type)) { - /* Pointer to pointer assignment is valid, if: + /* Implicit pointer-to-pointer conversion is valid, if: ** - both point to the same types, or ** - the rhs pointer is a void pointer, or ** - the lhs pointer is a void pointer. */ - if (!IsTypeVoid (IndirectConst (NewType)) && !IsTypeVoid (Indirect (Expr->Type))) { - /* Compare the types */ - switch (TypeCmp (NewType, Expr->Type)) { - - case TC_INCOMPATIBLE: - HasWarning = 1; - Msg = "Incompatible pointer assignment to '%s' from '%s'"; - /* Use the pointer type in the diagnostic */ - OldType = Expr->Type; - break; - - case TC_QUAL_DIFF: - HasWarning = 1; - Msg = "Pointer assignment to '%s' from '%s' discards qualifiers"; - /* Use the pointer type in the diagnostic */ - OldType = Expr->Type; - break; - - default: - /* Ok */ - break; - } + if (Result.C <= TC_PTR_INCOMPATIBLE || + (Result.F & TCF_INCOMPATIBLE_QUAL) != 0) + { + HasWarning = 1; + Msg = "Incompatible pointer conversion to '%s' from '%s'"; + /* Use the pointer type in the diagnostic */ + OldType = Expr->Type; + } else if ((Result.F & TCF_PTR_QUAL_DIFF) != 0) { + HasWarning = 1; + Msg = "Pointer conversion to '%s' from '%s' discards qualifiers"; + /* Use the pointer type in the diagnostic */ + OldType = Expr->Type; } } else if (IsClassInt (Expr->Type)) { - /* Int to pointer assignment is valid only for constant zero */ + /* Int to pointer conversion is valid only for constant zero */ if (!ED_IsConstAbsInt (Expr) || Expr->IVal != 0) { Warning ("Converting integer to pointer without a cast"); } @@ -291,11 +256,12 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) HasError = 1; } - } else { + } else if (Result.C < TC_IDENTICAL) { /* Invalid automatic conversion */ HasError = 1; } + /* Set default diagnostic message */ if (Msg == 0) { Msg = "Converting to '%s' from '%s'"; } @@ -350,13 +316,13 @@ void TypeCast (ExprDesc* Expr) /* Convert functions and arrays to "pointer to" object */ Expr->Type = PtrConversion (Expr->Type); - if (TypeCmp (NewType, Expr->Type) >= TC_QUAL_DIFF) { - /* If the new type only differs in qualifiers, just use it to - ** replace the old one. + if (TypeCmp (NewType, Expr->Type).C >= TC_PTR_INCOMPATIBLE) { + /* If the new type has the same underlying presentation, just + ** use it to replace the old one. */ ReplaceType (Expr, NewType); } else if (IsCastType (Expr->Type)) { - /* Convert the value. The rsult has always the new type */ + /* Convert the value. The result has always the new type */ DoConversion (Expr, NewType); } else { TypeCompatibilityDiagnostic (NewType, Expr->Type, 1, @@ -379,3 +345,142 @@ void TypeCast (ExprDesc* Expr) /* The result is always an rvalue */ ED_MarkExprAsRVal (Expr); } + + + +static void CompositeFuncParamList (const FuncDesc* F1, const FuncDesc* F2) +/* Composite two function symbol tables regarding function parameters */ +{ + /* Get the symbol tables */ + const SymTable* Tab1 = F1->SymTab; + const SymTable* Tab2 = F2->SymTab; + + /* Composite the parameter lists */ + const SymEntry* Sym1 = Tab1->SymHead; + const SymEntry* Sym2 = Tab2->SymHead; + + /* Composite the fields */ + while (Sym1 && (Sym1->Flags & SC_PARAM) && Sym2 && (Sym2->Flags & SC_PARAM)) { + + /* Get the symbol types */ + Type* Type1 = Sym1->Type; + Type* Type2 = Sym2->Type; + + /* If either of both functions is old style, apply the default + ** promotions to the parameter type. + */ + if (F1->Flags & FD_OLDSTYLE) { + if (IsClassInt (Type1)) { + Type1 = IntPromotion (Type1); + } + } + if (F2->Flags & FD_OLDSTYLE) { + if (IsClassInt (Type2)) { + Type2 = IntPromotion (Type2); + } + } + + /* Compose this field */ + TypeComposition (Type1, Type2); + + /* Get the pointers to the next fields */ + Sym1 = Sym1->NextSym; + Sym2 = Sym2->NextSym; + } +} + + + +void TypeComposition (Type* lhs, const Type* rhs) +/* Recursively compose two types into lhs. The two types must have compatible +** type or this fails with a critical check. +*/ +{ + FuncDesc* F1; + FuncDesc* F2; + long LeftCount, RightCount; + + /* Composite two types */ + while (lhs->C != T_END) { + + /* Check if the end of the type string is reached */ + if (rhs->C == T_END) { + break; + } + + /* Check for sanity */ + CHECK (GetUnderlyingTypeCode (lhs) == GetUnderlyingTypeCode (rhs)); + + /* Check for special type elements */ + if (IsTypeFunc (lhs)) { + /* Composite the function descriptors */ + F1 = GetFuncDesc (lhs); + F2 = GetFuncDesc (rhs); + + /* If one of both functions has an empty parameter list (which + ** does also mean, it is not a function definition, because the + ** flag is reset in this case), it is replaced by the other + ** definition, provided that the other has no default + ** promotions in the parameter list. If none of both parameter + ** lists is empty, we have to composite the parameter lists and + ** other attributes. + */ + if ((F1->Flags & FD_EMPTY) == FD_EMPTY) { + if ((F2->Flags & FD_EMPTY) == 0) { + /* Copy the parameters and flags */ + TypeCopy (lhs, rhs); + F1->Flags = F2->Flags; + } + } else if ((F2->Flags & FD_EMPTY) == 0) { + /* Composite the parameter lists */ + CompositeFuncParamList (F1, F2); + } + } else if (IsTypeArray (lhs)) { + /* Check member count */ + LeftCount = GetElementCount (lhs); + RightCount = GetElementCount (rhs); + + /* Set composite type if it is requested */ + if (LeftCount != UNSPECIFIED) { + SetElementCount (lhs, LeftCount); + } else if (RightCount != UNSPECIFIED) { + SetElementCount (lhs, RightCount); + } + } else { + /* Combine the qualifiers */ + if (IsClassPtr (lhs)) { + ++lhs; + ++rhs; + lhs->C |= GetQualifier (rhs); + } + } + + /* Next type string element */ + ++lhs; + ++rhs; + } + + return; +} + + + +FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType) +/* Refine the existing function descriptor with a new one */ +{ + FuncDesc* Old = GetFuncDesc (OldType); + FuncDesc* New = GetFuncDesc (NewType); + + CHECK (Old != 0 && New != 0); + + if ((New->Flags & FD_EMPTY) == 0) { + if ((Old->Flags & FD_EMPTY) == 0) { + TypeComposition (OldType, NewType); + } else { + TypeCopy (OldType, NewType); + Old->Flags &= ~FD_EMPTY; + } + } + + return Old; +} diff --git a/src/cc65/typeconv.h b/src/cc65/typeconv.h index 0a82eac27..839c5e43e 100644 --- a/src/cc65/typeconv.h +++ b/src/cc65/typeconv.h @@ -49,9 +49,6 @@ -void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg); -/* Print error or warning message about type conversion with proper type names */ - void TypeConversion (ExprDesc* Expr, const Type* NewType); /* Do an automatic conversion of the given expression to the new type. Output ** warnings or errors where this automatic conversion is suspicious or @@ -61,6 +58,14 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType); void TypeCast (ExprDesc* Expr); /* Handle an explicit cast. */ +void TypeComposition (Type* lhs, const Type* rhs); +/* Recursively compose two types into lhs. The two types must have compatible +** type or this fails with a critical check. +*/ + +FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType); +/* Refine the existing function descriptor with a new one */ + /* End of typeconv.h */