diff --git a/src/checker.c b/src/checker.c index 762a63a..d0b3c58 100644 --- a/src/checker.c +++ b/src/checker.c @@ -336,7 +336,7 @@ b32 canCoerce(Type *type, Type *target, CheckerContext *ctx) { if (TypesIdentical(type, target)) return true; if (target->kind == TypeKind_Any) return true; - if (isTuple(type) && ArrayLen(type->Tuple.types) == 1) { + if (isTuple(type) && type->Tuple.numTypes == 1) { return canCoerce(type->Tuple.types[0], target, ctx); } @@ -435,6 +435,8 @@ b32 representValueSilently(CheckerContext *ctx, Expr *expr, Type *type, Type *ta Conversion conversion(Type *type, Type *target) { Conversion result = 0; + if (type == target) return ConversionKind_None; + if (type->kind == target->kind) { result |= ConversionKind_Same; switch (type->kind) { @@ -453,7 +455,7 @@ Conversion conversion(Type *type, Type *target) { } } - if (type->kind == TypeKind_Tuple && ArrayLen(type->Tuple.types) == 1) { + if (type->kind == TypeKind_Tuple && type->Tuple.numTypes == 1) { return conversion(type->Tuple.types[0], target); } @@ -472,7 +474,7 @@ Conversion conversion(Type *type, Type *target) { if (IsFloat(type) && IsInteger(target)) { result |= ConversionKind_FtoI & ConversionFlag_Float; - if (IsSigned(type)) result |= ConversionFlag_Signed; + if (IsSigned(target)) result |= ConversionFlag_Signed; return result; } @@ -490,7 +492,10 @@ Conversion conversion(Type *type, Type *target) { // TODO: function to pointer and vica versa - PANIC("Unhandled or prohibited conversion"); + if (type == InvalidType || target == InvalidType) return ConversionKind_None; + + // TODO: ConversionKind_Invalid for non developer releases? + ASSERT_MSG(false, "Unhandled or prohibited conversion"); return ConversionKind_None; } @@ -503,6 +508,10 @@ void changeTypeOrMarkConversionForExpr(Expr *expr, Type *type, Type *target, Pac CheckerInfoForExpr(pkg, expr)->BasicExpr.type = target; break; + case ExprKind_Call: + // Do nothing intentionally + break; + default: CheckerInfoForExpr(pkg, expr)->coerce = conversion(type, target); } @@ -887,9 +896,10 @@ Type *checkExprTypeFunction(Expr *expr, CheckerContext *ctx, Package *pkg) { ArrayPush(params, type); } - DynamicArray(Type *) returnTypes = NULL; - ArrayFit(returnTypes, ArrayLen(func.result)); + DynamicArray(Type *) results = NULL; + ArrayFit(results, ArrayLen(func.result)); + bool isVoid = false; ForEach(func.result, Expr *) { Type *type = checkExpr(it, ¶mCtx, pkg); if (paramCtx.mode == ExprMode_Invalid) goto error; @@ -898,12 +908,26 @@ Type *checkExprTypeFunction(Expr *expr, CheckerContext *ctx, Package *pkg) { if (!expectType(pkg, type, ¶mCtx, it->start)) { isInvalid = true; } - ArrayPush(returnTypes, type); + ArrayPush(results, type); + + isVoid |= type == VoidType; + } + + if (isVoid) { + if (ArrayLen(func.result) > 1) { + ReportError(pkg, InvalidUseOfVoidError, expr->start, + "Void must be a functions only return type"); + } else { + ArrayFree(results); + } } if (isInvalid) goto error; - Type *type = NewTypeFunction(flags, params, returnTypes); + Type *type = NewTypeFunction(flags, params, results); + + ArrayFree(params); + ArrayFree(results); ctx->mode = ExprMode_Type; storeInfoBasicExpr(pkg, expr, type, ctx); @@ -924,12 +948,10 @@ Type *checkExprLitFunction(Expr *expr, CheckerContext *ctx, Package *pkg) { Scope *parameterScope = pushScope(pkg, ctx->scope); CheckerContext paramCtx = { parameterScope }; - DynamicArray(Type *) paramTypes = NULL; - DynamicArray(Type *) resultTypes = NULL; - DynamicArray(Symbol *) paramSymbols = NULL; - ArrayFit(paramTypes, ArrayLen(func.type->TypeFunction.params)); - ArrayFit(resultTypes, ArrayLen(func.type->TypeFunction.result)); - ArrayFit(paramSymbols, ArrayLen(func.type->TypeFunction.params)); + DynamicArray(Type *) params = NULL; + DynamicArray(Type *) results = NULL; + ArrayFit(params, ArrayLen(func.type->TypeFunction.params)); + ArrayFit(results, ArrayLen(func.type->TypeFunction.result)); TypeFlag typeFlags = TypeFlag_None; @@ -943,7 +965,6 @@ Type *checkExprLitFunction(Expr *expr, CheckerContext *ctx, Package *pkg) { Symbol *symbol; declareSymbol(pkg, parameterScope, it->key->Ident.name, &symbol, (Decl *) it); symbol->kind = SymbolKind_Variable; - ArrayPush(paramSymbols, symbol); Type *type = checkExpr(it->value, ¶mCtx, pkg); if (!expectType(pkg, type, ¶mCtx, it->value->start)) continue; @@ -954,35 +975,45 @@ Type *checkExprLitFunction(Expr *expr, CheckerContext *ctx, Package *pkg) { typeFlags |= type->Flags & TypeFlag_Variadic; typeFlags |= type->Flags & TypeFlag_CVargs; - ArrayPush(paramTypes, type); + ArrayPush(params, type); } + bool isVoid = false; ForEach(func.type->TypeFunction.result, Expr *) { Type *type = checkExpr(it, ¶mCtx, pkg); if (!expectType(pkg, type, ¶mCtx, it->start)) continue; - ArrayPush(resultTypes, type); - if (type == VoidType) { - if (ArrayLen(func.type->TypeFunction.result) > 1) { - ReportError(pkg, InvalidUseOfVoidError, it->start, - "Void must be a functions only return type"); - } - } + ArrayPush(results, type); + isVoid |= type == VoidType; } + Type *type = NewTypeFunction(typeFlags, params, results); + + ArrayFree(params); + ArrayFree(results); + + storeInfoBasicExpr(pkg, expr, type, ctx); + Scope *bodyScope = pushScope(pkg, parameterScope); - Type *tuple = VoidType; - if (resultTypes[0] != VoidType) { - tuple = NewTypeTuple(TypeFlag_None, resultTypes); + CheckerContext bodyCtx = { bodyScope }; + + if (isVoid) { + if (ArrayLen(func.type->TypeFunction.result) > 1) { + ReportError(pkg, InvalidUseOfVoidError, expr->LitFunction.type->start, + "Void must be a functions only return type"); + } else { + ArrayFree(results); + bodyCtx.desiredType = VoidType; + } + } else { + bodyCtx.desiredType = NewTypeTupleFromFunctionResults(TypeFlag_None, type->Function); } - CheckerContext bodyCtx = { bodyScope, .desiredType = tuple }; - ForEach(func.body->stmts, Stmt *) { - checkStmt(it, &bodyCtx, pkg); + size_t len = ArrayLen(func.body->stmts); + for (size_t i = 0; i < len; i++) { + checkStmt(func.body->stmts[i], &bodyCtx, pkg); if (bodyCtx.mode == ExprMode_Unresolved) goto unresolved; } - Type *type = NewTypeFunction(typeFlags, paramTypes, resultTypes); - storeInfoBasicExpr(pkg, expr, type, ctx); ctx->flags |= CheckerContextFlag_Constant; ctx->mode = ExprMode_Value; @@ -1071,7 +1102,7 @@ Type *checkExprLitCompound(Expr *expr, CheckerContext *ctx, Package *pkg) { } else { switch (type->kind) { case TypeKind_Struct: { - TypeField *field = type->Struct.members[currentIndex]; + TypeField *field = &type->Struct.members[currentIndex]; it->info = field; expectedValueType = field->type; break; @@ -1227,30 +1258,33 @@ Type *checkExprTypeSlice(Expr *expr, CheckerContext *ctx, Package *pkg) { Type *checkExprTypeStruct(Expr *expr, CheckerContext *ctx, Package *pkg) { ASSERT(expr->kind == ExprKind_TypeStruct); - DynamicArray(TypeField *) fields = NULL; + DynamicArray(TypeField) fields = NULL; ArrayFit(fields, ArrayLen(expr->TypeStruct.items)); u32 align = 0; u32 width = 0; - for (size_t i = 0; i < ArrayLen(expr->TypeStruct.items); i++) { + size_t numItems = ArrayLen(expr->TypeStruct.items); + for (size_t i = 0; i < numItems; i++) { AggregateItem item = expr->TypeStruct.items[i]; Type *type = checkExpr(item.type, ctx, pkg); align = MAX(align, type->Align); - for (size_t j = 0; j < ArrayLen(item.names); j++) { + + size_t numNames = ArrayLen(item.names); + for (size_t j = 0; j < numNames; j++) { // TODO: Check for duplicate names! // TODO: Check the max alignment of the Target Arch and cap alignment requirements to that - TypeField *field = AllocAst(pkg, sizeof(TypeField)); - ArrayPush(fields, field); - field->name = item.names[j]; - field->type = type; - field->offset = ALIGN_UP(width, type->Align); - width = field->offset + type->Width; + u32 offset = ALIGN_UP(width, type->Align); + TypeField field = {item.names[j], type, offset}; + width = offset + type->Width; + ArrayPush(fields, field); } } Type *type = NewTypeStruct(align, width, TypeFlag_None, fields); + ArrayFree(fields); + storeInfoBasicExpr(pkg, expr, type, ctx); ctx->mode = ExprMode_Type; return type; @@ -1670,7 +1704,7 @@ Type *checkExprCall(Expr *expr, CheckerContext *ctx, Package *pkg) { return checkExprCast(expr, ctx, pkg); } - size_t nParams = ArrayLen(calleeType->Function.params); + size_t nParams = calleeType->Function.numParams; CheckerContext argCtx = { ctx->scope }; ForEachWithIndex(expr->Call.args, i, Expr_KeyValue *, arg) { if (i >= nParams) { @@ -1687,7 +1721,7 @@ Type *checkExprCall(Expr *expr, CheckerContext *ctx, Package *pkg) { coerceType(arg->value, &argCtx, &type, calleeType->Function.params[i], pkg); } // TODO: Implement checking for calls - Type *type = NewTypeTuple(TypeFlag_None, calleeType->Function.results); + Type *type = NewTypeTupleFromFunctionResults(TypeFlag_None, calleeType->Function); storeInfoBasicExpr(pkg, expr, type, ctx); ctx->mode = ExprMode_Value; return type; @@ -2105,59 +2139,100 @@ void checkStmtAssign(Stmt *stmt, CheckerContext *ctx, Package *pkg) { ForEach(assign.lhs, Expr *) { Type *type = checkExpr(it, ctx, pkg); - ArrayPush(lhsTypes, type); - if (ctx->mode < ExprMode_Addressable && ctx->mode != ExprMode_Invalid) { - ReportError(pkg, ValueNotAssignableError, it->start, - "Cannot assign to value %s of type %s", DescribeExpr(it), DescribeType(type)); + // FIXME: Check for null + if (type->kind == TypeKind_Enum) { + ForEach(type->Tuple.types, Type *) { + ArrayPush(lhsTypes, it); + } + } else { + ArrayPush(lhsTypes, type); + if (ctx->mode < ExprMode_Addressable && ctx->mode != ExprMode_Invalid) { + ReportError(pkg, ValueNotAssignableError, it->start, + "Cannot assign to value %s of type %s", DescribeExpr(it), DescribeType(type)); + } } } - if (assign.rhs[0]->kind == ExprKind_Call) { - // TODO: Handle void calls (empty tuple) (error) - // TODO: Handle single returns - // TODO: Handle multiple returns - UNIMPLEMENTED(); - return; - } - Type *prevDesiredType = ctx->desiredType; - ForEachWithIndex(assign.rhs, i, Expr *, expr) { - if (i >= ArrayLen(lhsTypes)) { - break; - } - ctx->desiredType = lhsTypes[i]; + size_t values = 0; + size_t numLhs = ArrayLen(assign.lhs); + size_t rhsIndex = 0; + for (size_t lhsIndex = 0; values < numLhs;) { + Expr *expr = assign.rhs[rhsIndex++]; + + ctx->desiredType = lhsTypes[lhsIndex]; Type *type = checkExpr(expr, ctx, pkg); + if (ctx->mode == ExprMode_Invalid) continue; + if (ctx->mode == ExprMode_Unresolved) goto unresolved; + + if (type->kind == TypeKind_Tuple) { + // Check each matches individually + for (size_t rhsIndex = 0; rhsIndex < type->Tuple.numTypes; rhsIndex++) { + Type *ty = type->Tuple.types[rhsIndex]; + Type *target = lhsTypes[lhsIndex]; + + // Coerce type is unable to attach coercions to calls. It doesn't work with tuple types. We do it here. + // The special thing about tuples is their conversions are stored on their receiving value. This is + // indicated by setting the tuple expression coerce to ConversionKind_Tuple outside the loop. + // We do this prior to calling coerce type as conversion handles invalid conversions + CheckerInfoForExpr(pkg, assign.lhs[lhsIndex])->coerce = conversion(ty, target); + + if (!coerceTypeSilently(expr, ctx, &ty, target, pkg)) { + ReportError(pkg, TypeMismatchError, expr->start, + "Cannot assign %s to value of type %s", DescribeType(ty), DescribeType(lhsTypes[lhsIndex])); + ReportNote(pkg, expr->start, + "Value comes from the %zu indexed result from call to %s", rhsIndex, DescribeExpr(expr)); + // TODO: Use the language value comes from the %zu (st|nd|rd|th) result of the call to %s + } + + lhsIndex += 1; + } + + values += type->Tuple.numTypes; + + // ConversionKind_Tuple indicates that the conversions are stored on the receiving value. + CheckerInfoForExpr(pkg, expr)->coerce = ConversionKind_Tuple; + continue; + } + if (ctx->mode < ExprMode_Value) { ReportError(pkg, NotAValueError, expr->start, "Expected a value but got %s (type %s)", DescribeExpr(expr), DescribeType(type)); + } else { + coerceType(expr, ctx, &type, lhsTypes[lhsIndex], pkg); } - if (!coerceType(expr, ctx, &type, lhsTypes[i], pkg)) { - ReportError(pkg, TypeMismatchError, expr->start, - "Cannot assign %s to value of type %s", DescribeType(type), DescribeType(lhsTypes[i])); - } + + values += 1; + lhsIndex += 1; } ctx->desiredType = prevDesiredType; - if (ArrayLen(assign.rhs) != ArrayLen(assign.lhs)) { + if (numLhs != values) { ReportError(pkg, AssignmentCountMismatchError, stmt->start, - "Left side has %zu values while right side %zu values", ArrayLen(assign.lhs), ArrayLen(assign.rhs)); + "Left side has %zu values while right side has %zu values", ArrayLen(assign.lhs), ArrayLen(assign.rhs)); } + + return; + +unresolved: + ctx->mode = ExprMode_Unresolved; + return; } void checkStmtReturn(Stmt *stmt, CheckerContext *ctx, Package *pkg) { ASSERT(stmt->kind == StmtKind_Return); ASSERT(ctx->desiredType && ctx->desiredType->kind == TypeKind_Tuple); - size_t nTypes = ArrayLen(ctx->desiredType->Tuple.types); + u32 nTypes = ctx->desiredType->Tuple.numTypes; size_t nExprs = ArrayLen(stmt->Return.exprs); // FIXME: What about returns that are tuples? We need a nice splat helper if (nExprs != nTypes) { ReportError(pkg, WrongNumberOfReturnsError, stmt->start, "Wrong number of return expressions, expected %zu, got %zu", - ArrayLen(ctx->desiredType->Tuple.types), ArrayLen(stmt->Return.exprs)); + nTypes, ArrayLen(stmt->Return.exprs)); } for (size_t i = 0; i < MIN(nTypes, nExprs); i++) { Expr *expr = stmt->Return.exprs[i]; @@ -2565,14 +2640,14 @@ info = GetStmtInfo(&pkg, stmt)->BasicExpr checkTypeFunction("fn () -> void"); ASSERT(type->kind == TypeKind_Function); ASSERT(info.type == type); - ASSERT(ArrayLen(type->Function.params) == 0); - ASSERT(ArrayLen(type->Function.results) == 1); + ASSERT(type->Function.numParams == 0); + ASSERT(type->Function.numResults == 0); checkTypeFunction("fn (u8, u8, u8, u8) -> (u8, u8, u8, u8)"); ASSERT(type->kind == TypeKind_Function); ASSERT(info.type == type); - ASSERT(ArrayLen(type->Function.params) == 4); - ASSERT(ArrayLen(type->Function.results) == 4); + ASSERT(type->Function.numParams == 4); + ASSERT(type->Function.numResults == 4); } void test_checkTypePointer() { @@ -2654,12 +2729,12 @@ info = GetStmtInfo(&pkg, stmt)->BasicExpr checkTypeStruct("struct {}"); ASSERT(type->kind == TypeKind_Struct); - ASSERT(!type->Struct.members); + ASSERT(type->Struct.numMembers == 0); ASSERT(!pkg.diagnostics.errors); checkTypeStruct("struct {a: u8}"); ASSERT(type->kind == TypeKind_Struct); - ASSERT(type->Struct.members[0]->type == U8Type); + ASSERT(type->Struct.members[0].type == U8Type); ASSERT(type->Struct.Flags == TypeFlag_None); ASSERT(type->Align == 8); ASSERT(type->Width == 8); @@ -2667,9 +2742,9 @@ info = GetStmtInfo(&pkg, stmt)->BasicExpr checkTypeStruct("struct {a: u8; b: u16}"); ASSERT(type->kind == TypeKind_Struct); - ASSERT(type->Struct.members[0]->type == U8Type); - ASSERT(type->Struct.members[1]->type == U16Type); - ASSERT_MSG(type->Struct.members[1]->offset == 16, "Fields should be aligned to at least their size"); + ASSERT(type->Struct.members[0].type == U8Type); + ASSERT(type->Struct.members[1].type == U16Type); + ASSERT_MSG(type->Struct.members[1].offset == 16, "Fields should be aligned to at least their size"); ASSERT(type->Struct.Flags == TypeFlag_None); ASSERT(type->Align == 16); ASSERT(type->Width == 32); @@ -2677,17 +2752,17 @@ info = GetStmtInfo(&pkg, stmt)->BasicExpr checkTypeStruct("struct {a: u8; b: u8; b: u16; c: u32; d: u32}"); ASSERT(type->kind == TypeKind_Struct); - ASSERT(type->Struct.members[0]->type == U8Type); - ASSERT(type->Struct.members[1]->type == U8Type); - ASSERT(type->Struct.members[2]->type == U16Type); - ASSERT(type->Struct.members[3]->type == U32Type); - ASSERT(type->Struct.members[4]->type == U32Type); - - ASSERT_MSG(type->Struct.members[0]->offset == 0, "Fields should be aligned to at least their size"); - ASSERT_MSG(type->Struct.members[1]->offset == 8, "Fields should be aligned to at least their size"); - ASSERT_MSG(type->Struct.members[2]->offset == 16, "Fields should be aligned to at least their size"); - ASSERT_MSG(type->Struct.members[3]->offset == 32, "Fields should be aligned to at least their size"); - ASSERT_MSG(type->Struct.members[4]->offset == 64, "Fields should be aligned to at least their size"); + ASSERT(type->Struct.members[0].type == U8Type); + ASSERT(type->Struct.members[1].type == U8Type); + ASSERT(type->Struct.members[2].type == U16Type); + ASSERT(type->Struct.members[3].type == U32Type); + ASSERT(type->Struct.members[4].type == U32Type); + + ASSERT_MSG(type->Struct.members[0].offset == 0, "Fields should be aligned to at least their size"); + ASSERT_MSG(type->Struct.members[1].offset == 8, "Fields should be aligned to at least their size"); + ASSERT_MSG(type->Struct.members[2].offset == 16, "Fields should be aligned to at least their size"); + ASSERT_MSG(type->Struct.members[3].offset == 32, "Fields should be aligned to at least their size"); + ASSERT_MSG(type->Struct.members[4].offset == 64, "Fields should be aligned to at least their size"); ASSERT(type->Struct.Flags == TypeFlag_None); ASSERT(type->Align == 32); ASSERT(type->Width == 96); @@ -3105,7 +3180,6 @@ void test_checkStmtReturn() { REINIT_COMPILER(); Stmt *stmt; CheckerContext ctx = { pkg.scope }; - DynamicArray(Type *) types = NULL; #define checkReturn(_CODE) \ stmt = resetAndParseReturningLastStmt(_CODE); \ @@ -3113,15 +3187,15 @@ checkStmtReturn(stmt, &ctx, &pkg); \ RESET_CONTEXT(ctx); \ ArrayClear(types) - ArrayPush(types, I64Type); - ctx.desiredType = NewTypeTuple(TypeFlag_None, types); + Type *types[3] = {I64Type, I64Type, F64Type}; + Type type = {TypeKind_Tuple, .Tuple = {TypeFlag_None, types, 1}}; + + ctx.desiredType = &type; checkReturn("return 42"); ASSERT(!pkg.diagnostics.errors); - ArrayPush(types, I64Type); - ArrayPush(types, I64Type); - ArrayPush(types, F64Type); - ctx.desiredType = NewTypeTuple(TypeFlag_None, types); + type.Tuple.numTypes = 3; + ctx.desiredType = &type; checkReturn("return 1, 2, 6.28"); ASSERT(!pkg.diagnostics.errors); } diff --git a/src/checker.h b/src/checker.h index 90b58cf..ea899c1 100644 --- a/src/checker.h +++ b/src/checker.h @@ -20,15 +20,16 @@ enum Enum_CheckerInfoKind { STATIC_ASSERT(_StmtKind_End <= UINT8_MAX, "enum values overflow storage type"); typedef u8 Conversion; -#define ConversionKind_Mask 0x07 // Lower 3 bits denote the class -#define ConversionKind_None 0 -#define ConversionKind_Same 1 -#define ConversionKind_FtoI 2 -#define ConversionKind_ItoF 3 -#define ConversionKind_PtoI 4 -#define ConversionKind_ItoP 5 -#define ConversionKind_Bool 6 -#define ConversionKind_Any 7 +#define ConversionKind_Mask 0x0F // Lower 3 bits denote the class +#define ConversionKind_None 0 +#define ConversionKind_Same 1 +#define ConversionKind_FtoI 2 +#define ConversionKind_ItoF 3 +#define ConversionKind_PtoI 4 +#define ConversionKind_ItoP 5 +#define ConversionKind_Bool 6 +#define ConversionKind_Tuple 7 // Information on the conversion can be found on their receiver. +#define ConversionKind_Any 15 #define ConversionFlag_Extend 0x10 // 0001 #define ConversionFlag_Signed 0x20 // 0010 diff --git a/src/header.c b/src/header.c index 1a2e574..106c55c 100644 --- a/src/header.c +++ b/src/header.c @@ -91,9 +91,10 @@ void cgenDecl(HeaderContext *ctx, DynamicArray(Expr_Ident *) names, Type *type, } ArrayPrintf(ctx->complexDecls, "struct %s {\n", it->name); - ForEach(type->Struct.members, TypeField *) { + for (u32 i = 0; i < type->Struct.numMembers; i++) { + TypeField it = type->Struct.members[i]; ArrayPrintf(ctx->complexDecls, " "); - cgenType(&ctx->complexDecls, it->name, it->type); + cgenType(&ctx->complexDecls, it.name, it.type); ArrayPrintf(ctx->complexDecls, ";\n"); } ArrayPrintf(ctx->complexDecls, "};\n"); diff --git a/src/llvm.cpp b/src/llvm.cpp index 4a29589..108be92 100644 --- a/src/llvm.cpp +++ b/src/llvm.cpp @@ -116,6 +116,8 @@ void emitStmt(Context *ctx, Stmt *stmt); llvm::Type *canonicalize(Context *ctx, Type *type) { switch (type->kind) { case TypeKind_Tuple: + // TODO: Should they? + PANIC("Tuples are a checker thing, they should not make their way into the backend, unless they do?"); if (!type->Tuple.types) { return llvm::Type::getVoidTy(ctx->m->getContext()); } @@ -142,15 +144,28 @@ llvm::Type *canonicalize(Context *ctx, Type *type) { } break; case TypeKind_Function: { - ASSERT_MSG(ArrayLen(type->Function.results) == 1, "Currently we don't support multi-return"); std::vector params; - For(type->Function.params) { + for (u32 i = 0; i < type->Function.numParams; i++) { llvm::Type *paramType = canonicalize(ctx, type->Function.params[i]); params.push_back(paramType); } - // TODO(Brett): canonicalize multi-return and Kai vargs - llvm::Type *returnType = canonicalize(ctx, type->Function.results[0]); + llvm::Type *returnType; + if (type->Function.numResults == 0) { + returnType = llvm::Type::getVoidTy(ctx->m->getContext()); + } else if (type->Function.numResults == 1) { + returnType = canonicalize(ctx, type->Function.results[0]); + } else { + std::vector elements; + for (u32 i = 0; i < type->Function.numResults; i++) { + llvm::Type *ty = canonicalize(ctx, type->Function.results[i]); + elements.push_back(ty); + } + + returnType = llvm::StructType::create(ctx->m->getContext(), elements); + } + + // TODO: Kai vargs return llvm::FunctionType::get(returnType, params, (type->Function.Flags & TypeFlag_CVargs) != 0); } break; @@ -167,9 +182,9 @@ llvm::Type *canonicalize(Context *ctx, Type *type) { ASSERT_MSG(!type->Symbol, "Only unnamed structures should get to here"); std::vector elements; - ForEachWithIndex(type->Struct.members, index, TypeField *, it) { - llvm::Type *type = canonicalize(ctx, it->type); - elements.push_back(type); + for (u32 i = 0; i < type->Struct.numMembers; i++) { + llvm::Type *ty = canonicalize(ctx, type->Struct.members[i].type); + elements.push_back(ty); } llvm::StructType *ty = llvm::StructType::create(ctx->m->getContext(), elements); @@ -235,9 +250,9 @@ llvm::DIType *debugCanonicalize(Context *ctx, Type *type) { // NOTE: Clang just uses a derived type that is a pointer // !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64) std::vector parameterTypes; - ForEach(type->Function.params, Type *) { - llvm::DIType *type = debugCanonicalize(ctx, it); - parameterTypes.push_back(type); + for (u32 i = 0; i < type->Function.numParams; i++) { + llvm::DIType *ty = debugCanonicalize(ctx, type->Function.params[i]); + parameterTypes.push_back(ty); } auto pTypes = ctx->d.builder->getOrCreateTypeArray(parameterTypes); @@ -251,17 +266,18 @@ llvm::DIType *debugCanonicalize(Context *ctx, Type *type) { } std::vector elementTypes; - ForEach(type->Struct.members, TypeField *) { - llvm::DIType *dtype = debugCanonicalize(ctx, it->type); + for (u32 i = 0; i < type->Struct.numMembers; i++) { + TypeField it = type->Struct.members[i]; + llvm::DIType *dtype = debugCanonicalize(ctx, it.type); auto member = ctx->d.builder->createMemberType( ctx->d.scope, - it->name, + it.name, ctx->d.file, 0, // TODO: LineNo for struct members - it->type->Width, - it->type->Align, - it->offset, + it.type->Width, + it.type->Align, + it.offset, llvm::DINode::DIFlags::FlagZero, dtype ); @@ -339,7 +355,15 @@ llvm::AllocaInst *createEntryBlockAlloca(Context *ctx, Symbol *sym) { return alloca; } +llvm::AllocaInst *creteEntryBlockAlloca(Context *ctx, llvm::Type *type, const char *name) { + llvm::IRBuilder<> b(&ctx->fn->getEntryBlock(), ctx->fn->getEntryBlock().begin()); + llvm::AllocaInst *alloca = b.CreateAlloca(type, 0, name); + return alloca; +} + llvm::Value *coerceValue(Context *ctx, Conversion conversion, llvm::Value *value, llvm::Type *target) { + ASSERT(target); + ASSERT(value); switch (conversion & ConversionKind_Mask) { case ConversionKind_None: return value; @@ -395,8 +419,12 @@ llvm::Value *coerceValue(Context *ctx, Conversion conversion, llvm::Value *value UNIMPLEMENTED(); return NULL; + case ConversionKind_Tuple: + return value; + default: - return NULL; + UNIMPLEMENTED(); + return value; } } @@ -440,11 +468,11 @@ llvm::Value *emitExprLitCompound(Context *ctx, Expr *expr) { case TypeKind_Struct: { llvm::StructType *type = (llvm::StructType *) canonicalize(ctx, info.type); - // FIXME: Determine if, like C all uninitialized Struct members are zero'd or left undefined + // FIXME: Determine if, like C, all uninitialized Struct members are zero'd or left undefined llvm::Value *agg = llvm::UndefValue::get(type); ForEach(expr->LitCompound.elements, Expr_KeyValue *) { TypeField *field = (TypeField *) it->info; - u64 index = (field - info.type->Struct.members[0]); + u64 index = (field - &info.type->Struct.members[0]); llvm::Value *val = emitExpr(ctx, it->value); agg = ctx->b.CreateInsertValue(agg, val, (u32) index); @@ -561,7 +589,11 @@ llvm::Value *emitExpr(Context *ctx, Expr *expr, llvm::Type *desiredType) { Expr_LitInt lit = expr->LitInt; CheckerInfo info = ctx->checkerInfo[expr->id]; Type *type = info.BasicExpr.type; - value = llvm::ConstantInt::get(canonicalize(ctx, type), lit.val, type->Flags & TypeFlag_Signed); + if (type->kind == TypeKind_Float) { + value = llvm::ConstantFP::get(canonicalize(ctx, type), lit.val); + } else { + value = llvm::ConstantInt::get(canonicalize(ctx, type), lit.val, type->Flags & TypeFlag_Signed); + } break; } @@ -617,7 +649,8 @@ llvm::Value *emitExpr(Context *ctx, Expr *expr, llvm::Type *desiredType) { break; } - if (ctx->checkerInfo[expr->id].coerce != ConversionKind_None) { + if (ctx->checkerInfo[expr->id].coerce != ConversionKind_None && desiredType) { + // If desired type is NULL and coerce is not then it maybe because we are emitting the lhs of a expr value = coerceValue(ctx, ctx->checkerInfo[expr->id].coerce, value, desiredType); } @@ -801,7 +834,7 @@ llvm::Value *emitExprSubscript(Context *ctx, Expr *expr) { llvm::Value *structPtr = emitExpr(ctx, expr->Subscript.expr); ctx->returnAddress = previousReturnAddress; llvm::Value *arrayPtr = ctx->b.CreateStructGEP(NULL, structPtr, 0); - aggregate = ctx->b.CreateAlignedLoad(arrayPtr, ctx->targetMachine->getPointerSize()); + aggregate = ctx->b.CreateAlignedLoad(arrayPtr, ctx->targetMachine->getPointerSize(0)); indicies.push_back(index); resultType = TypeFromCheckerInfo(recvInfo)->Slice.elementType; } break; @@ -906,14 +939,20 @@ llvm::Function *emitExprLitFunction(Context *ctx, Expr *expr, llvm::Function *fn // Setup return block - // TODO: multiple returns (we should change TypeFunction to have a single possibly tuple return type) - Type *retType = info.BasicExpr.type->Function.results[0]; ctx->retBlock = llvm::BasicBlock::Create(ctx->m->getContext(), "return", fn); - if (!fn->getReturnType()->isVoidTy()) { - ctx->retValue = createEntryBlockAlloca(ctx, retType, "result"); + + if (info.BasicExpr.type->Function.results) { + llvm::IRBuilder<> b(entry, entry->begin()); + llvm::AllocaInst *alloca = b.CreateAlloca(fn->getReturnType(), 0, "result"); + if (info.BasicExpr.type->Function.numResults == 1) { + alloca->setAlignment(BytesFromBits(info.BasicExpr.type->Function.results[0]->Align)); + } + ctx->retValue = alloca; } - ForEach(expr->LitFunction.body->stmts, Stmt *) { - emitStmt(ctx, it); + + size_t len = ArrayLen(expr->LitFunction.body->stmts); + for (size_t i = 0; i < len; i++) { + emitStmt(ctx, expr->LitFunction.body->stmts[i]); } if (!ctx->b.GetInsertBlock()->getTerminator()) { @@ -942,11 +981,17 @@ llvm::Function *emitExprLitFunction(Context *ctx, Expr *expr, llvm::Function *fn ctx->b.SetInsertPoint(ctx->retBlock); } - if (fn->getReturnType()->isVoidTy()) { - ctx->b.CreateRetVoid(); - } else { + if (info.BasicExpr.type->Function.numResults == 1) { + Type *retType = info.BasicExpr.type->Function.results[0]; auto retValue = ctx->b.CreateAlignedLoad(ctx->retValue, BytesFromBits(retType->Align)); ctx->b.CreateRet(retValue); + } else if (info.BasicExpr.type->Function.numResults) { + llvm::StructType *retType = llvm::dyn_cast(fn->getReturnType()); + ASSERT_MSG(retType, "Expect a function with multiple returns to have a struct return type"); + auto retValue = ctx->b.CreateAlignedLoad(ctx->retValue, ctx->dataLayout.getStructLayout(retType)->getAlignment()); + ctx->b.CreateRet(retValue); + } else { + ctx->b.CreateRetVoid(); } // FIXME: Return to previous scope @@ -986,7 +1031,7 @@ llvm::StructType *emitExprTypeStruct(Context *ctx, Expr *expr) { u32 index = 0; for (size_t i = 0; i < ArrayLen(expr->TypeStruct.items); i++) { AggregateItem item = expr->TypeStruct.items[i]; - Type *fieldType = type->Struct.members[index]->type; + Type *fieldType = type->Struct.members[index].type; llvm::Type *ty = canonicalize(ctx, fieldType); llvm::DIType *dty = debugCanonicalize(ctx, fieldType); @@ -999,7 +1044,7 @@ llvm::StructType *emitExprTypeStruct(Context *ctx, Expr *expr) { item.start.line, fieldType->Width, fieldType->Align, - type->Struct.members[index]->offset, + type->Struct.members[index].offset, llvm::DINode::DIFlags::FlagZero, dty ); @@ -1053,9 +1098,8 @@ llvm::StructType *emitExprTypeStruct(Context *ctx, Expr *expr) { #if DEBUG // Checks the frontend layout matches the llvm backend const llvm::StructLayout *layout = ctx->dataLayout.getStructLayout(ty); - size_t numElements = ArrayLen(type->Struct.members); - for (u32 i = 0; i < numElements; i++) { - ASSERT(layout->getElementOffsetInBits(i) == type->Struct.members[i]->offset); + for (u32 i = 0; i < type->Struct.numMembers; i++) { + ASSERT(layout->getElementOffsetInBits(i) == type->Struct.members[i].offset); ASSERT(layout->getSizeInBits() == type->Width); ASSERT(layout->getAlignment() == type->Align / 8); } @@ -1303,18 +1347,55 @@ void emitStmtAssign(Context *ctx, Stmt *stmt) { ASSERT(stmt->kind == StmtKind_Assign); Stmt_Assign assign = stmt->Assign; - if (ArrayLen(assign.lhs) > 1 && ArrayLen(assign.rhs) == 1 && assign.rhs[0]->kind == ExprKind_Call) { -// llvm::Value *value = emitExpr(ctx, assign.rhs[0]); - UNIMPLEMENTED(); - } - ForEachWithIndex(assign.lhs, i, Expr *, it) { - ctx->returnAddress = true; // FIXME: restore previous value. - llvm::Value *lhs = emitExpr(ctx, it); - ctx->returnAddress = false; - llvm::Value *rhs = emitExpr(ctx, assign.rhs[i]); - Type *type = TypeFromCheckerInfo(ctx->checkerInfo[assign.lhs[i]->id]); - debugPos(ctx, assign.start); - ctx->b.CreateAlignedStore(rhs, lhs, BytesFromBits(type->Align)); + size_t numLhs = ArrayLen(assign.lhs); + size_t rhsIndex = 0; + for (size_t lhsIndex = 0; lhsIndex < numLhs;) { + Expr *expr = assign.rhs[rhsIndex++]; + llvm::Value *rhs = emitExpr(ctx, expr); + + Type *exprType = ctx->checkerInfo[expr->id].BasicExpr.type; + if (expr->kind == ExprKind_Call) { + size_t numValues = exprType->Tuple.numTypes; + if (numValues == 1) { + ctx->returnAddress = true; + llvm::Value *lhs = emitExpr(ctx, assign.lhs[lhsIndex++]); + ctx->returnAddress = false; + debugPos(ctx, assign.start); + ctx->b.CreateAlignedStore(rhs, lhs, BytesFromBits(exprType->Tuple.types[0]->Align)); + } else { + // create some stack space to land the returned struct onto + llvm::Value *resultAddress = creteEntryBlockAlloca(ctx, rhs->getType(), ""); + ctx->b.CreateStore(rhs, resultAddress); + + for (size_t resultIndex = 0; resultIndex < numValues; resultIndex++) { + Expr *lhsExpr = assign.lhs[lhsIndex++]; + ctx->returnAddress = true; + llvm::Value *lhs = emitExpr(ctx, lhsExpr); + ctx->returnAddress = false; + debugPos(ctx, assign.start); + + llvm::Value *addr = ctx->b.CreateStructGEP(rhs->getType(), resultAddress, (u32) resultIndex); + llvm::Value *val = ctx->b.CreateLoad(addr); + + CheckerInfo lhsInfo = ctx->checkerInfo[lhsExpr->id]; + // Conversions of tuples like this are stored on the lhs + if (lhsInfo.coerce != ConversionKind_None) { + ASSERT(lhs->getType()->isPointerTy()); + val = coerceValue(ctx, lhsInfo.coerce, val, lhs->getType()->getPointerElementType()); + } + + ctx->b.CreateAlignedStore(val, lhs, BytesFromBits(TypeFromCheckerInfo(lhsInfo)->Align)); + } + } + } else { + Expr *lhsExpr = assign.lhs[lhsIndex++]; + ctx->returnAddress = true; + llvm::Value *lhs = emitExpr(ctx, lhsExpr); + ctx->returnAddress = false; + Type *type = TypeFromCheckerInfo(ctx->checkerInfo[lhsExpr->id]); + debugPos(ctx, assign.start); + ctx->b.CreateAlignedStore(rhs, lhs, BytesFromBits(type->Align)); + } } } @@ -1342,18 +1423,26 @@ void emitStmtIf(Context *ctx, Stmt *stmt) { void emitStmtReturn(Context *ctx, Stmt *stmt) { ASSERT(stmt->kind == StmtKind_Return); - if (ArrayLen(stmt->Return.exprs) != 1) UNIMPLEMENTED(); + + size_t numReturns = ArrayLen(stmt->Return.exprs); + std::vector values; - ForEach(stmt->Return.exprs, Expr *) { - // FIXME: getReturnType will no suffice when we support multireturn - auto value = emitExpr(ctx, it, ctx->fn->getReturnType()); + for (size_t i = 0; i < numReturns; i++) { + Expr *it = stmt->Return.exprs[i]; + llvm::Type *desiredType = ctx->fn->getReturnType(); + if (numReturns > 1) { + llvm::StructType *type = llvm::dyn_cast(ctx->fn->getReturnType()); + ASSERT_MSG(type, "Expected the context struct type for function with multiple return vals"); + desiredType = type->getElementType((u32) i); + } + auto value = emitExpr(ctx, it, desiredType); values.push_back(value); } clearDebugPos(ctx); if (values.size() > 1) { for (u32 idx = 0; idx < values.size(); idx++) { - std::vector idxs = {0, idx}; - ctx->retValue = ctx->b.CreateInsertValue(ctx->retValue, values[idx], idxs); + llvm::Value *elPtr = ctx->b.CreateStructGEP(ctx->fn->getReturnType(), ctx->retValue, idx); + ctx->b.CreateStore(values[idx], elPtr); } } else if (values.size() == 1) { ctx->b.CreateStore(values[0], ctx->retValue); @@ -1785,7 +1874,7 @@ b32 emitObjectFile(Package *p, char *name, Context *ctx) { llvm::legacy::PassManager pass; llvm::TargetMachine::CodeGenFileType fileType = llvm::TargetMachine::CGFT_ObjectFile; - if (ctx->targetMachine->addPassesToEmitFile(pass, dest, fileType)) { + if (ctx->targetMachine->addPassesToEmitFile(pass, dest, nullptr, fileType)) { llvm::errs() << "TargetMachine can't emit a file of this type"; return 1; } diff --git a/src/types.c b/src/types.c index d90f5b5..817fc75 100644 --- a/src/types.c +++ b/src/types.c @@ -118,10 +118,10 @@ StructFieldLookupResult StructFieldLookup(Type_Struct type, const char *name) { u32 index = 0; TypeField *field = NULL; - ForEachWithIndex(type.members, i, TypeField *, it) { - if (it->name == name) { - index = (u32) i; - field = it; + for (u32 i = 0; i < type.numMembers; i++) { + if (type.members[i].name == name) { + index = i; + field = &type.members[i]; break; } } @@ -218,12 +218,35 @@ Type *NewTypeArray(TypeFlag flags, u64 length, Type *elementType) { Map internFunctionTypes; Type *NewTypeFunction(TypeFlag flags, DynamicArray(Type *) params, DynamicArray(Type *) results) { - u64 hash = HashMix(HashBytes(params, ArrayLen(params) * sizeof(params)), HashBytes(results, ArrayLen(results) * sizeof(results))); + ASSERT(ArrayLen(params) < UINT32_MAX); + ASSERT(ArrayLen(results) < UINT32_MAX); + + u32 numParams = (u32) ArrayLen(params); + u32 numResults = (u32) ArrayLen(results); + + bool isVoid = numResults == 1 && results[0] == VoidType; + if (isVoid) { + numResults = 0; + results = NULL; + } + + u64 hash = HashMix(HashBytes(params, numParams * sizeof(params)), HashBytes(results, numResults * sizeof(results))); u64 key = hash ? hash : 1; InternType *intern = MapGet(&internFunctionTypes, (void*) key); for (InternType *it = intern; it; it = it->next) { Type *type = it->type; - if (ArraysEqual(params, type->Function.params) && ArraysEqual(results, type->Function.results) && flags == type->Function.Flags) { + + bool nParamsEql = numParams == type->Function.numParams; + bool nResultsEql = numResults == type->Function.numResults; + bool flagsEql = flags == type->Function.Flags; + bool paramsPtrEql = params == type->Function.params; + bool resultsPtrEql = results == type->Function.results; + + if (nParamsEql && nResultsEql && flagsEql && + ((paramsPtrEql && resultsPtrEql) || + (memcmp(params, type->Function.params, numParams) == 0 && + memcmp(results, type->Function.results, numResults) == 0))) + { return type; } } @@ -231,30 +254,46 @@ Type *NewTypeFunction(TypeFlag flags, DynamicArray(Type *) params, DynamicArray( type->Width = TargetTypeMetrics[TargetMetrics_Pointer].Width; type->Align = TargetTypeMetrics[TargetMetrics_Pointer].Align; type->Flags = flags; - type->Function.params = params; - type->Function.results = results; - InternType *newIntern = Alloc(DefaultAllocator, sizeof(InternType)); + + Type **p = Alloc(DefaultAllocator, numParams * sizeof *p); + Type **r = Alloc(DefaultAllocator, numResults * sizeof *r); + + type->Function.params = memcpy(p, params, numParams * sizeof *p); + type->Function.results = memcpy(r, results, numResults * sizeof *p); + + type->Function.numParams = numParams; + type->Function.numResults = numResults; + + InternType *newIntern = Alloc(DefaultAllocator, sizeof *newIntern); newIntern->type = type; newIntern->next = intern; MapSet(&internFunctionTypes, (void*) key, newIntern); return type; } -Type *NewTypeTuple(TypeFlag flags, DynamicArray(Type *) types) { +Type *NewTypeTupleFromFunctionResults(TypeFlag flags, Type_Function function) { Type *type = AllocType(TypeKind_Tuple); type->Flags = flags; - type->Tuple.types = types; - type->Width = 0; - type->Align = 0; + + type->Tuple.numTypes = function.numResults; + type->Tuple.types = function.results; return type; } -Type *NewTypeStruct(u32 Align, u32 Width, TypeFlag flags, DynamicArray(TypeField *) members) { +Type *NewTypeStruct(u32 Align, u32 Width, TypeFlag flags, DynamicArray(TypeField) members) { + ASSERT(ArrayLen(members) < UINT32_MAX); + + u32 numMembers = (u32) ArrayLen(members); + Type *type = AllocType(TypeKind_Struct); type->Align = Align; type->Width = Width; type->Flags = flags; - type->Struct.members = members; + + type->Struct.members = Alloc(DefaultAllocator, sizeof(TypeField) * numMembers); + memcpy(type->Struct.members, members, sizeof(TypeField) * numMembers); + + type->Struct.numMembers = numMembers; return type; } @@ -286,6 +325,16 @@ void declareBuiltinType(const char *name, Type *type) { type->Symbol = symbol; } +void declareBuiltinTypeAlias(const char *name, Type *type, Type *alias) { + name = StrIntern(name); + Symbol *symbol; + declareSymbol(&builtinPackage, builtinPackage.scope, name, &symbol, NULL); + symbol->state = SymbolState_Resolved; + symbol->type = type; + symbol->kind = SymbolKind_Type; + alias->Symbol = symbol; +} + bool HaveInitializedBuiltins = false; void InitBuiltins() { if (HaveInitializedBuiltins) return; @@ -323,7 +372,7 @@ void InitBuiltins() { _global = Alloc(DefaultAllocator, sizeof(Type)); \ memcpy(_global, _alias, sizeof(Type)); \ _global->Flags |= TypeFlag_Alias; \ - declareBuiltinType(_name, _alias) + declareBuiltinTypeAlias(_name, _alias, _global) TYPE(InvalidType, "", Invalid, 0, TypeFlag_None); TYPE(FileType, "", Invalid, 0, TypeFlag_None); diff --git a/src/types.h b/src/types.h index 6c5ce74..64d70f2 100644 --- a/src/types.h +++ b/src/types.h @@ -72,9 +72,6 @@ typedef u8 TypeFlag; // Enum #define TypeFlag_EnumFlags 0x1 -// Struct -#define TypeFlag_StructTuple 0x1 - // Tuple #define TypeFlag_NoReturn 0x1 @@ -96,8 +93,10 @@ struct Type_Array { struct Type_Function { TypeFlag Flags; - DynamicArray(Type *) params; - DynamicArray(Type *) results; + Type **params; + Type **results; + u32 numParams; + u32 numResults; }; typedef struct TypeField TypeField; @@ -109,14 +108,16 @@ struct TypeField { struct Type_Struct { TypeFlag Flags; - DynamicArray(TypeField *) members; + TypeField *members; + u32 numMembers; }; struct Type_Union { TypeFlag Flags; u32 tagWidth; u32 dataWidth; - DynamicArray(Type *) cases; + Type **cases; + u32 numCases; }; struct Type_Enum { @@ -125,7 +126,8 @@ struct Type_Enum { struct Type_Tuple { TypeFlag Flags; - DynamicArray(Type *) types; + Type **types; + u32 numTypes; }; STATIC_ASSERT(offsetof(Type_Pointer, Flags) == 0, "Flags must be at offset 0");