diff options
| -rw-r--r-- | source/slang/check.cpp | 454 | ||||
| -rw-r--r-- | source/slang/emit.cpp | 96 | ||||
| -rw-r--r-- | source/slang/expr-defs.h | 6 | ||||
| -rw-r--r-- | source/slang/lower.cpp | 42 | ||||
| -rw-r--r-- | source/slang/modifier-defs.h | 8 | ||||
| -rw-r--r-- | source/slang/parser.cpp | 20 | ||||
| -rw-r--r-- | source/slang/slang-stdlib.cpp | 209 | ||||
| -rw-r--r-- | source/slang/syntax-base-defs.h | 12 | ||||
| -rw-r--r-- | source/slang/syntax.cpp | 6 | ||||
| -rw-r--r-- | source/slang/syntax.h | 45 |
10 files changed, 667 insertions, 231 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp index cc2954fc4..ce9b2de55 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -140,14 +140,14 @@ namespace Slang } RefPtr<Expr> ConstructDeclRefExpr( - DeclRef<Decl> declRef, + DeclRef<Decl> declRef, RefPtr<Expr> baseExpr, - RefPtr<Expr> originalExpr) + SourceLoc loc) { if (baseExpr) { auto expr = new MemberExpr(); - expr->loc = originalExpr->loc; + expr->loc = loc; expr->BaseExpression = baseExpr; expr->name = declRef.GetName(); expr->type = GetTypeForDeclRef(declRef); @@ -157,7 +157,7 @@ namespace Slang else { auto expr = new VarExpr(); - expr->loc = originalExpr->loc; + expr->loc = loc; expr->name = declRef.GetName(); expr->type = GetTypeForDeclRef(declRef); expr->declRef = declRef; @@ -166,14 +166,14 @@ namespace Slang } RefPtr<Expr> ConstructDerefExpr( - RefPtr<Expr> base, - RefPtr<Expr> originalExpr) + RefPtr<Expr> base, + SourceLoc loc) { auto ptrLikeType = base->type->As<PointerLikeType>(); SLANG_ASSERT(ptrLikeType); auto derefExpr = new DerefExpr(); - derefExpr->loc = originalExpr->loc; + derefExpr->loc = loc; derefExpr->base = base; derefExpr->type = QualType(ptrLikeType->elementType); @@ -183,9 +183,9 @@ namespace Slang } RefPtr<Expr> ConstructLookupResultExpr( - LookupResultItem const& item, - RefPtr<Expr> baseExpr, - RefPtr<Expr> originalExpr) + LookupResultItem const& item, + RefPtr<Expr> baseExpr, + SourceLoc loc) { // If we collected any breadcrumbs, then these represent // additional segments of the lookup path that we need @@ -196,28 +196,28 @@ namespace Slang switch (breadcrumb->kind) { case LookupResultItem::Breadcrumb::Kind::Member: - bb = ConstructDeclRefExpr(breadcrumb->declRef, bb, originalExpr); + bb = ConstructDeclRefExpr(breadcrumb->declRef, bb, loc); break; case LookupResultItem::Breadcrumb::Kind::Deref: - bb = ConstructDerefExpr(bb, originalExpr); + bb = ConstructDerefExpr(bb, loc); break; default: SLANG_UNREACHABLE("all cases handle"); } } - return ConstructDeclRefExpr(item.declRef, bb, originalExpr); + return ConstructDeclRefExpr(item.declRef, bb, loc); } RefPtr<Expr> createLookupResultExpr( - LookupResult const& lookupResult, - RefPtr<Expr> baseExpr, - RefPtr<Expr> originalExpr) + LookupResult const& lookupResult, + RefPtr<Expr> baseExpr, + SourceLoc loc) { if (lookupResult.isOverloaded()) { auto overloadedExpr = new OverloadedExpr(); - overloadedExpr->loc = originalExpr->loc; + overloadedExpr->loc = loc; overloadedExpr->type = QualType( getSession()->getOverloadedType()); overloadedExpr->base = baseExpr; @@ -226,7 +226,7 @@ namespace Slang } else { - return ConstructLookupResultExpr(lookupResult.item, baseExpr, originalExpr); + return ConstructLookupResultExpr(lookupResult.item, baseExpr, loc); } } @@ -264,7 +264,7 @@ namespace Slang } // otherwise, we had a single decl and it was valid, hooray! - return ConstructLookupResultExpr(lookupResult.item, overloadedExpr->base, overloadedExpr); + return ConstructLookupResultExpr(lookupResult.item, overloadedExpr->base, overloadedExpr->loc); } RefPtr<Expr> ExpectATypeRepr(RefPtr<Expr> expr) @@ -583,90 +583,6 @@ namespace Slang public: - typedef unsigned int ConversionCost; - enum : ConversionCost - { - // No conversion at all - kConversionCost_None = 0, - - // Conversions based on explicit sub-typing relationships are the cheapest - // - // TODO(tfoley): We will eventually need a discipline for ranking - // when two up-casts are comparable. - kConversionCost_CastToInterface = 50, - - // Conversion that is lossless and keeps the "kind" of the value the same - kConversionCost_RankPromotion = 100, - - // Conversions that are lossless, but change "kind" - kConversionCost_UnsignedToSignedPromotion = 200, - - // Conversion from signed->unsigned integer of same or greater size - kConversionCost_SignedToUnsignedConversion = 300, - - // Cost of converting an integer to a floating-point type - kConversionCost_IntegerToFloatConversion = 400, - - // Catch-all for conversions that should be discouraged - // (i.e., that really shouldn't be made implicitly) - // - // TODO: make these conversions not be allowed implicitly in "Slang mode" - kConversionCost_GeneralConversion = 900, - - // Additional conversion cost to add when promoting from a scalar to - // a vector (this will be added to the cost, if any, of converting - // the element type of the vector) - kConversionCost_ScalarToVector = 1, - }; - - enum BaseTypeConversionKind : uint8_t - { - kBaseTypeConversionKind_Signed, - kBaseTypeConversionKind_Unsigned, - kBaseTypeConversionKind_Float, - kBaseTypeConversionKind_Error, - }; - - enum BaseTypeConversionRank : uint8_t - { - kBaseTypeConversionRank_Bool, - kBaseTypeConversionRank_Int8, - kBaseTypeConversionRank_Int16, - kBaseTypeConversionRank_Int32, - kBaseTypeConversionRank_IntPtr, - kBaseTypeConversionRank_Int64, - kBaseTypeConversionRank_Error, - }; - - struct BaseTypeConversionInfo - { - BaseTypeConversionKind kind; - BaseTypeConversionRank rank; - }; - static BaseTypeConversionInfo GetBaseTypeConversionInfo(BaseType baseType) - { - switch (baseType) - { - #define CASE(TAG, KIND, RANK) \ - case BaseType::TAG: { BaseTypeConversionInfo info = {kBaseTypeConversionKind_##KIND, kBaseTypeConversionRank_##RANK}; return info; } break - - CASE(Bool, Unsigned, Bool); - CASE(Int, Signed, Int32); - CASE(UInt, Unsigned, Int32); - CASE(UInt64, Unsigned, Int64); - CASE(Half, Float, Int16); - CASE(Float, Float, Int32); - CASE(Double, Float, Int64); - CASE(Void, Error, Error); - - #undef CASE - - default: - break; - } - SLANG_UNREACHABLE("all cases handled"); - } - bool ValuesAreEqual( RefPtr<IntVal> left, RefPtr<IntVal> right) @@ -692,6 +608,19 @@ namespace Slang return false; } + // Compute the cost of using a particular declaration to + // perform implicit type conversion. + ConversionCost getImplicitConversionCost( + Decl* decl) + { + if(auto modifier = decl->FindModifier<ImplicitConversionModifier>()) + { + return modifier->cost; + } + + return kConversionCost_Explicit; + } + // Central engine for implementing implicit coercion logic bool TryCoerceImpl( RefPtr<Type> toType, // the target type for conversion @@ -836,6 +765,7 @@ namespace Slang // +#if 0 if (auto toBasicType = toType->AsBasicType()) { if (auto fromBasicType = fromType->AsBasicType()) @@ -951,6 +881,7 @@ namespace Slang } } } +#endif if (auto toDeclRefType = toType->As<DeclRefType>()) { @@ -971,7 +902,97 @@ namespace Slang } } - // TODO: more cases! + // Look for an initializer/constructor declaration in the target type, + // which is marked as usable for implicit conversion, and which takes + // the source type as an argument. + + OverloadResolveContext overloadContext; + + List<RefPtr<Expr>> args; + args.Add(fromExpr); + + overloadContext.disallowNestedConversions = true; + overloadContext.argCount = 1; + overloadContext.argTypes = &fromType; + + overloadContext.originalExpr = nullptr; + if(fromExpr) + { + overloadContext.loc = fromExpr->loc; + overloadContext.funcLoc = fromExpr->loc; + overloadContext.args = &fromExpr; + } + + overloadContext.baseExpr = nullptr; + overloadContext.mode = OverloadResolveContext::Mode::JustTrying; + + AddTypeOverloadCandidates(toType, overloadContext); + + if(overloadContext.bestCandidates.Count() != 0) + { + // There were multiple candidates that were equally good. + + // First, we will check if these candidates are even applicable. + // If they aren't, then they can't be used for conversion. + if(overloadContext.bestCandidates[0].status != OverloadCandidate::Status::Appicable) + return false; + + // If we reach this point, then we have multiple candidates which are + // all equally applicable, which means we have an ambiguity. + // If the user is just querying whether a conversion is possible, we + // will tell them it is, because ambiguity should trigger an ambiguity + // error, and not a "no conversion possible" error. + + // We will compute a nominal conversion cost as the minimum over + // all the conversions available. + ConversionCost cost = kConversionCost_GeneralConversion; + for(auto candidate : overloadContext.bestCandidates) + { + ConversionCost candidateCost = getImplicitConversionCost( + candidate.item.declRef.getDecl()); + + if(candidateCost < cost) + cost = candidateCost; + } + + if(outCost) + *outCost = cost; + + if(outToExpr) + { + // The user is asking for us to actually perform the conversion, + // so we need to generate an appropriate expression here. + + throw "foo bar baz"; + } + + return true; + } + else if(overloadContext.bestCandidate) + { + // There is a single best candidate for conversion. + + // It might not actually be usable, so let's check that first. + if(overloadContext.bestCandidate->status != OverloadCandidate::Status::Appicable) + return false; + + // Okay, it is applicable, and we just need to let the user + // know about it, and optionally construct a call. + + // We need to extract the conversion cost from the candidate we found. + ConversionCost cost = getImplicitConversionCost( + overloadContext.bestCandidate->item.declRef.getDecl());; + + if(outCost) + *outCost = cost; + + if(outToExpr) + { + *outToExpr = CompleteOverloadCandidate(overloadContext, *overloadContext.bestCandidate); + } + + return true; + } return false; } @@ -991,7 +1012,7 @@ namespace Slang } RefPtr<Expr> CreateImplicitCastExpr( - RefPtr<Type> toType, + RefPtr<Type> toType, RefPtr<Expr> fromExpr) { // In "rewrite" mode, we will generate a different syntax node @@ -1007,10 +1028,17 @@ namespace Slang castExpr = new ImplicitCastExpr(); } + auto typeType = new TypeType(); + typeType->type = toType; + + auto typeExpr = new SharedTypeExpr(); + typeExpr->type.type = typeType; + typeExpr->base.type = toType; + castExpr->loc = fromExpr->loc; - castExpr->TargetType.type = toType; + castExpr->FunctionExpr = typeExpr; castExpr->type = QualType(toType); - castExpr->Expression = fromExpr; + castExpr->Arguments.Add(fromExpr); return castExpr; } @@ -2272,7 +2300,7 @@ namespace Slang } else if(auto castExpr = dynamic_cast<TypeCastExpr*>(expr)) { - auto val = TryConstantFoldExpr(castExpr->Expression.Ptr()); + auto val = TryConstantFoldExpr(castExpr->Arguments[0].Ptr()); if(val) return val; } @@ -2457,7 +2485,7 @@ namespace Slang } RefPtr<Expr> subscriptFuncExpr = createLookupResultExpr( - lookupResult, subscriptExpr->BaseExpression, subscriptExpr); + lookupResult, subscriptExpr->BaseExpression, subscriptExpr->loc); // Now that we know there is at least one subscript member, // we will construct a reference to it and try to call it @@ -2982,7 +3010,32 @@ namespace Slang ForReal, }; - RefPtr<AppExprBase> appExpr; + // Location to use when reporting overload-resolution errors. + SourceLoc loc; + + // The original expression (if any) that triggered things + RefPtr<Expr> originalExpr; + + // Source location of the "function" part of the expression, if any + SourceLoc funcLoc; + + // The original arguments to the call + UInt argCount = 0; + RefPtr<Expr>* args = nullptr; + RefPtr<Type>* argTypes = nullptr; + + UInt getArgCount() { return argCount; } + RefPtr<Expr>& getArg(UInt index) { return args[index]; } + RefPtr<Type>& getArgType(UInt index) + { + if(argTypes) + return argTypes[index]; + else + return getArg(index)->type.type; + } + + bool disallowNestedConversions = false; + RefPtr<Expr> baseExpr; // Are we still trying out candidates, or are we @@ -3060,7 +3113,7 @@ namespace Slang OverloadResolveContext& context, OverloadCandidate const& candidate) { - UInt argCount = context.appExpr->Arguments.Count(); + UInt argCount = context.getArgCount(); ParamCounts paramCounts = { 0, 0 }; switch (candidate.flavor) { @@ -3087,7 +3140,7 @@ namespace Slang { if (!isRewriteMode()) { - getSink()->diagnose(context.appExpr, Diagnostics::notEnoughArguments, argCount, paramCounts.required); + getSink()->diagnose(context.loc, Diagnostics::notEnoughArguments, argCount, paramCounts.required); } } else @@ -3095,7 +3148,7 @@ namespace Slang SLANG_ASSERT(argCount > paramCounts.allowed); if (!isRewriteMode()) { - getSink()->diagnose(context.appExpr, Diagnostics::tooManyArguments, argCount, paramCounts.allowed); + getSink()->diagnose(context.loc, Diagnostics::tooManyArguments, argCount, paramCounts.allowed); } } } @@ -3107,7 +3160,7 @@ namespace Slang OverloadResolveContext& context, OverloadCandidate const& candidate) { - auto expr = context.appExpr; + auto expr = context.originalExpr; auto decl = candidate.item.declRef.decl; @@ -3120,7 +3173,7 @@ namespace Slang { if (!isRewriteMode()) { - getSink()->diagnose(context.appExpr, Diagnostics::expectedPrefixOperator); + getSink()->diagnose(context.loc, Diagnostics::expectedPrefixOperator); getSink()->diagnose(decl, Diagnostics::seeDefinitionOf, decl->getName()); } } @@ -3136,7 +3189,7 @@ namespace Slang { if (!isRewriteMode()) { - getSink()->diagnose(context.appExpr, Diagnostics::expectedPostfixOperator); + getSink()->diagnose(context.loc, Diagnostics::expectedPostfixOperator); getSink()->diagnose(decl, Diagnostics::seeDefinitionOf, decl->getName()); } } @@ -3155,8 +3208,6 @@ namespace Slang OverloadResolveContext& context, OverloadCandidate& candidate) { - auto& args = context.appExpr->Arguments; - auto genericDeclRef = candidate.item.declRef.As<GenericDecl>(); int aa = 0; @@ -3164,7 +3215,7 @@ namespace Slang { if (auto typeParamRef = memberRef.As<GenericTypeParamDecl>()) { - auto arg = args[aa++]; + auto arg = context.getArg(aa++); if (context.mode == OverloadResolveContext::Mode::JustTrying) { @@ -3180,7 +3231,7 @@ namespace Slang } else if (auto valParamRef = memberRef.As<GenericValueParamDecl>()) { - auto arg = args[aa++]; + auto arg = context.getArg(aa++); if (context.mode == OverloadResolveContext::Mode::JustTrying) { @@ -3210,8 +3261,7 @@ namespace Slang OverloadResolveContext& context, OverloadCandidate& candidate) { - auto& args = context.appExpr->Arguments; - UInt argCount = args.Count(); + UInt argCount = context.getArgCount(); List<DeclRef<ParamDecl>> params; switch (candidate.flavor) @@ -3234,13 +3284,20 @@ namespace Slang for (UInt ii = 0; ii < argCount; ++ii) { - auto& arg = args[ii]; + auto& arg = context.getArg(ii); + auto argType = context.getArgType(ii); auto param = params[ii]; if (context.mode == OverloadResolveContext::Mode::JustTrying) { ConversionCost cost = kConversionCost_None; - if (!CanCoerce(GetType(param), arg->type, &cost)) + if( context.disallowNestedConversions ) + { + // We need an exact match in this case. + if(!GetType(param)->Equals(argType)) + return false; + } + else if (!CanCoerce(GetType(param), argType, &cost)) { return false; } @@ -3288,28 +3345,31 @@ namespace Slang // Create the representation of a given generic applied to some arguments RefPtr<Expr> CreateGenericDeclRef( - RefPtr<Expr> baseExpr, - RefPtr<AppExprBase> appExpr) + RefPtr<Expr> baseExpr, + RefPtr<Expr> originalExpr, + UInt argCount, + RefPtr<Expr> const* args) { auto baseDeclRefExpr = baseExpr.As<DeclRefExpr>(); if (!baseDeclRefExpr) { SLANG_DIAGNOSE_UNEXPECTED(getSink(), baseExpr, "expected a reference to a generic declaration"); - return CreateErrorExpr(appExpr.Ptr()); + return CreateErrorExpr(originalExpr); } auto baseGenericRef = baseDeclRefExpr->declRef.As<GenericDecl>(); if (!baseGenericRef) { SLANG_DIAGNOSE_UNEXPECTED(getSink(), baseExpr, "expected a reference to a generic declaration"); - return CreateErrorExpr(appExpr.Ptr()); + return CreateErrorExpr(originalExpr); } RefPtr<Substitutions> subst = new Substitutions(); subst->genericDecl = baseGenericRef.getDecl(); subst->outer = baseGenericRef.substitutions; - for (auto arg : appExpr->Arguments) + for(UInt aa = 0; aa < argCount; ++aa) { + auto arg = args[aa]; subst->args.Add(ExtractGenericArgVal(arg)); } @@ -3318,7 +3378,7 @@ namespace Slang return ConstructDeclRefExpr( innerDeclRef, nullptr, - appExpr); + originalExpr->loc); } // Take an overload candidate that previously got through @@ -3334,11 +3394,11 @@ namespace Slang // special case for generic argument inference failure if (candidate.status == OverloadCandidate::Status::GenericArgumentInferenceFailed) { - String callString = GetCallSignatureString(context.appExpr); + String callString = getCallSignatureString(context); if (!isRewriteMode()) { getSink()->diagnose( - context.appExpr, + context.loc, Diagnostics::genericArgumentInferenceFailed, callString); @@ -3349,7 +3409,6 @@ namespace Slang } context.mode = OverloadResolveContext::Mode::ForReal; - context.appExpr->type = QualType(getSession()->getErrorType()); if (!TryCheckOverloadCandidateArity(context, candidate)) goto error; @@ -3365,41 +3424,66 @@ namespace Slang { auto baseExpr = ConstructLookupResultExpr( - candidate.item, context.baseExpr, context.appExpr->FunctionExpr); + candidate.item, context.baseExpr, context.funcLoc); switch(candidate.flavor) { case OverloadCandidate::Flavor::Func: - context.appExpr->FunctionExpr = baseExpr; - context.appExpr->type = QualType(candidate.resultType); - - // A call may yield an l-value, and we should take a look at the candidate to be sure - if(auto subscriptDeclRef = candidate.item.declRef.As<SubscriptDecl>()) { - for(auto setter : subscriptDeclRef.getDecl()->getMembersOfType<SetterDecl>()) + RefPtr<AppExprBase> callExpr = context.originalExpr.As<InvokeExpr>(); + if(!callExpr) { - context.appExpr->type.IsLeftValue = true; + callExpr = new InvokeExpr(); + callExpr->loc = context.loc; + + for(UInt aa = 0; aa < context.argCount; ++aa) + callExpr->Arguments.Add(context.getArg(aa)); + } + + + callExpr->FunctionExpr = baseExpr; + callExpr->type = QualType(candidate.resultType); + + // A call may yield an l-value, and we should take a look at the candidate to be sure + if(auto subscriptDeclRef = candidate.item.declRef.As<SubscriptDecl>()) + { + for(auto setter : subscriptDeclRef.getDecl()->getMembersOfType<SetterDecl>()) + { + callExpr->type.IsLeftValue = true; + } } - } - // TODO: there may be other cases that confer l-value-ness + // TODO: there may be other cases that confer l-value-ness + + return callExpr; + } - return context.appExpr; break; case OverloadCandidate::Flavor::Generic: - return CreateGenericDeclRef(baseExpr, context.appExpr); + return CreateGenericDeclRef(baseExpr, context.originalExpr, + context.argCount, + context.args); break; default: - SLANG_DIAGNOSE_UNEXPECTED(getSink(), context.appExpr, "unknown overload candidate flavor"); + SLANG_DIAGNOSE_UNEXPECTED(getSink(), context.loc, "unknown overload candidate flavor"); break; } } error: - return CreateErrorExpr(context.appExpr.Ptr()); + + if(context.originalExpr) + { + return CreateErrorExpr(context.originalExpr.Ptr()); + } + else + { + SLANG_DIAGNOSE_UNEXPECTED(getSink(), context.loc, "no original expression for overload result"); + return nullptr; + } } // Implement a comparison operation between overload candidates, @@ -3859,6 +3943,7 @@ namespace Slang } } +#if 0 bool TryUnifyArgAndParamTypes( ConstraintSystem& system, RefPtr<Expr> argExpr, @@ -3869,6 +3954,7 @@ namespace Slang // an overload group... return TryUnifyTypes(system, argExpr->type, GetType(paramDeclRef)); } +#endif // Take a generic declaration and try to specialize its parameters // so that the resulting inner declaration can be applicable in @@ -3889,10 +3975,9 @@ namespace Slang // to match it up with the arguments accordingly... if (auto funcDeclRef = unspecializedInnerRef.As<CallableDecl>()) { - auto& args = context.appExpr->Arguments; auto params = GetParameters(funcDeclRef).ToArray(); - UInt argCount = args.Count(); + UInt argCount = context.getArgCount(); UInt paramCount = params.Count(); // Bail out on mismatch. @@ -3926,7 +4011,7 @@ namespace Slang // So the question is then whether a mismatch during the // unification step should be taken as an immediate failure... - TryUnifyArgAndParamTypes(constraints, args[aa], params[aa]); + TryUnifyTypes(constraints, context.getArgType(aa), GetType(params[aa])); #endif } } @@ -4225,21 +4310,28 @@ namespace Slang return getDeclSignatureString(item.declRef); } - String GetCallSignatureString(RefPtr<AppExprBase> expr) + String getCallSignatureString( + OverloadResolveContext& context) { - StringBuilder argsListBuilder; + StringBuilder argsListBuilder; argsListBuilder << "("; - bool first = true; - for (auto a : expr->Arguments) + + UInt argCount = context.getArgCount(); + for( UInt aa = 0; aa < argCount; ++aa ) { - if (!first) argsListBuilder << ", "; - argsListBuilder << a->type->ToString(); - first = false; + if(aa != 0) argsListBuilder << ", "; + argsListBuilder << context.getArgType(aa)->ToString(); } argsListBuilder << ")"; return argsListBuilder.ProduceString(); } +#if 0 + String GetCallSignatureString(RefPtr<AppExprBase> expr) + { + return getCallSignatureString(expr->Arguments); + } +#endif RefPtr<Expr> ResolveInvoke(InvokeExpr * expr) { @@ -4262,7 +4354,14 @@ namespace Slang } OverloadResolveContext context; - context.appExpr = expr; + + context.originalExpr = expr; + context.funcLoc = funcExpr->loc; + + context.argCount = expr->Arguments.Count(); + context.args = expr->Arguments.Buffer(); + context.loc = expr->loc; + if (auto funcMemberExpr = funcExpr.As<MemberExpr>()) { context.baseExpr = funcMemberExpr->BaseExpression; @@ -4297,7 +4396,7 @@ namespace Slang else if(auto baseMemberRef = funcExpr.As<MemberExpr>()) funcName = baseMemberRef->name; - String argsList = GetCallSignatureString(expr); + String argsList = getCallSignatureString(context); if (context.bestCandidates[0].status != OverloadCandidate::Status::Appicable) { @@ -4457,7 +4556,12 @@ namespace Slang // Otherwise, let's start looking at how to find an overload... OverloadResolveContext context; - context.appExpr = genericAppExpr; + context.originalExpr = genericAppExpr; + context.funcLoc = baseExpr->loc; + context.argCount = args.Count(); + context.args = args.Buffer(); + context.loc = genericAppExpr->loc; + context.baseExpr = GetBaseExpr(baseExpr); AddGenericOverloadCandidates(baseExpr, context); @@ -4606,7 +4710,7 @@ namespace Slang return createLookupResultExpr( lookupResult, nullptr, - expr); + expr->loc); } if (!isRewriteMode()) @@ -4619,6 +4723,29 @@ namespace Slang RefPtr<Expr> visitTypeCastExpr(TypeCastExpr * expr) { + // Check the term we are applying first + auto funcExpr = expr->FunctionExpr; + funcExpr = CheckTerm(funcExpr); + + // Now ensure that the term represnets a (proper) type. + TypeExp typeExp; + typeExp.exp = funcExpr; + typeExp = CheckProperType(typeExp); + + expr->FunctionExpr = typeExp.exp; + expr->type.type = typeExp.type; + + // Next check the argument expression (there should be only one) + for (auto & arg : expr->Arguments) + { + arg = CheckExpr(arg); + } + + // Now process this like any other explicit call (so casts + // and constructor calls are semantically equivalent). + return CheckInvokeExprWithCheckedOperands(expr); + +#if 0 expr->Expression = CheckTerm(expr->Expression); auto targetType = CheckProperType(expr->TargetType); expr->TargetType = targetType; @@ -4659,6 +4786,7 @@ namespace Slang } expr->type = QualType(getSession()->getErrorType()); return expr; +#endif } // Get the type to use when referencing a declaration @@ -4906,7 +5034,7 @@ namespace Slang return createLookupResultExpr( lookupResult, expr->BaseExpression, - expr); + expr->loc); } } } @@ -4928,7 +5056,7 @@ namespace Slang return createLookupResultExpr( lookupResult, expr->BaseExpression, - expr); + expr->loc); } // catch-all diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index 6440c63a3..2deb0cea7 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -1367,7 +1367,7 @@ struct EmitVisitor { if(auto typeCastExpr = expr.As<TypeCastExpr>()) { - expr = typeCastExpr->Expression; + expr = typeCastExpr->Arguments[0]; } // TODO: any other cases? else @@ -1519,14 +1519,66 @@ struct EmitVisitor return bestModifier; } + void emitSimpleCallArgs( + RefPtr<InvokeExpr> callExpr) + { + Emit("("); + UInt argCount = callExpr->Arguments.Count(); + for (UInt aa = 0; aa < argCount; ++aa) + { + if (aa != 0) Emit(", "); + EmitExpr(callExpr->Arguments[aa]); + } + Emit(")"); + } + + void emitSimpleConstructorCallExpr( + RefPtr<InvokeExpr> callExpr, + EOpInfo outerPrec) + { + if(context->shared->target == CodeGenTarget::HLSL) + { + // HLSL needs to special-case a constructor call with a single argument. + if(callExpr->Arguments.Count() == 1) + { + auto prec = kEOp_Prefix; + bool needClose = MaybeEmitParens(outerPrec, prec); + + Emit("("); + EmitType(callExpr->type); + Emit(") "); + + EmitExprWithPrecedence(callExpr->Arguments[0], rightSide(outerPrec, prec)); + + if(needClose) Emit(")"); + return; + } + } + + + // Default handling is to emit what amounts to an ordinary call, + // but using the type of the expression directly as the "function" to call. + auto prec = kEOp_Postfix; + bool needClose = MaybeEmitParens(outerPrec, prec); + + EmitType(callExpr->type); + + emitSimpleCallArgs(callExpr); + + if (needClose) + { + Emit(")"); + } + } + // Emit a call expression that doesn't involve any special cases, // just an expression of the form `f(a0, a1, ...)` void emitSimpleCallExpr( RefPtr<InvokeExpr> callExpr, EOpInfo outerPrec) { - auto prec = kEOp_Postfix; - bool needClose = MaybeEmitParens(outerPrec, prec); + // We will first check if this represents a constructor call, + // since those may need to be handled differently. auto funcExpr = callExpr->FunctionExpr; if (auto funcDeclRefExpr = funcExpr.As<DeclRefExpr>()) @@ -1534,29 +1586,20 @@ struct EmitVisitor auto declRef = funcDeclRefExpr->declRef; if (auto ctorDeclRef = declRef.As<ConstructorDecl>()) { - // We really want to emit a reference to the type begin constructed - EmitType(callExpr->type); - } - else - { - // default case: just emit the decl ref - EmitExprWithPrecedence(funcExpr, leftSide(outerPrec, prec)); + emitSimpleConstructorCallExpr(callExpr, outerPrec); + return; } } - else - { - // default case: just emit the expression - EmitExprWithPrecedence(funcExpr, leftSide(outerPrec, prec)); - } - Emit("("); - UInt argCount = callExpr->Arguments.Count(); - for (UInt aa = 0; aa < argCount; ++aa) - { - if (aa != 0) Emit(", "); - EmitExpr(callExpr->Arguments[aa]); - } - Emit(")"); + // Once we've ruled out constructor calls, we can move on + // to just emitting an ordinary calll expression. + + auto prec = kEOp_Postfix; + bool needClose = MaybeEmitParens(outerPrec, prec); + + EmitExprWithPrecedence(funcExpr, leftSide(outerPrec, prec)); + + emitSimpleCallArgs(callExpr); if (needClose) { @@ -2416,11 +2459,15 @@ struct EmitVisitor { // This was an implicit cast inserted in code parsed in "rewriter" mode, // so we don't want to output it and change what the user's code looked like. - ExprVisitorWithArg::dispatch(castExpr->Expression, arg); + ExprVisitorWithArg::dispatch(castExpr->Arguments[0], arg); } void visitTypeCastExpr(TypeCastExpr* castExpr, ExprEmitArg const& arg) { + // We emit a type cast expression as a constructor call + emitSimpleConstructorCallExpr(castExpr, arg.outerPrec); + +#if 0 bool needClose = false; switch(context->shared->target) { @@ -2449,6 +2496,7 @@ struct EmitVisitor break; } if(needClose) Emit(")"); +#endif } void visitInitializerListExpr(InitializerListExpr* expr, ExprEmitArg const&) diff --git a/source/slang/expr-defs.h b/source/slang/expr-defs.h index 5e3d23a02..5821bddc3 100644 --- a/source/slang/expr-defs.h +++ b/source/slang/expr-defs.h @@ -102,9 +102,9 @@ SYNTAX_CLASS(DerefExpr, Expr) END_SYNTAX_CLASS() // Any operation that performs type-casting -SYNTAX_CLASS(TypeCastExpr, Expr) - SYNTAX_FIELD(TypeExp, TargetType) - SYNTAX_FIELD(RefPtr<Expr>, Expression) +SYNTAX_CLASS(TypeCastExpr, InvokeExpr) +// SYNTAX_FIELD(TypeExp, TargetType) +// SYNTAX_FIELD(RefPtr<Expr>, Expression) END_SYNTAX_CLASS() // An explicit type-cast that appear in the user's code with `(type) expr` syntax diff --git a/source/slang/lower.cpp b/source/slang/lower.cpp index 7a487d29f..f0d756a02 100644 --- a/source/slang/lower.cpp +++ b/source/slang/lower.cpp @@ -1781,19 +1781,9 @@ struct LoweringVisitor LoweredExpr visitInvokeExpr( InvokeExpr* expr) { - return LoweredExpr(lowerCallExpr(new InvokeExpr(), expr)); - } - - LoweredExpr visitInfixExpr( - InfixExpr* expr) - { - return LoweredExpr(lowerCallExpr(new InfixExpr(), expr)); - } - - LoweredExpr visitPrefixExpr( - PrefixExpr* expr) - { - return LoweredExpr(lowerCallExpr(new PrefixExpr(), expr)); + // Create a clone with the same class + InvokeExpr* loweredExpr = (InvokeExpr*) expr->getClass().createInstance(); + return LoweredExpr(lowerCallExpr(loweredExpr, expr)); } LoweredExpr visitSelectExpr( @@ -1804,12 +1794,6 @@ struct LoweringVisitor return LoweredExpr(lowerCallExpr(new SelectExpr(), expr)); } - LoweredExpr visitPostfixExpr( - PostfixExpr* expr) - { - return LoweredExpr(lowerCallExpr(new PostfixExpr(), expr)); - } - LoweredExpr visitDerefExpr( DerefExpr* expr) { @@ -2412,15 +2396,29 @@ struct LoweringVisitor assign(expr, LoweredExpr(createVarRef(getPosition(expr), varDecl))); } + RefPtr<Expr> createTypeExpr( + RefPtr<Type> type) + { + auto typeType = new TypeType(); + typeType->type = type; + + auto result = new SharedTypeExpr(); + result->base.type = type; + result->type.type = typeType; + + return result; + } + RefPtr<Expr> createCastExpr( - RefPtr<Type> type, + RefPtr<Type> type, RefPtr<Expr> expr) { RefPtr<ExplicitCastExpr> castExpr = new ExplicitCastExpr(); castExpr->loc = expr->loc; castExpr->type.type = type; - castExpr->TargetType.type = type; - castExpr->Expression = expr; + + castExpr->FunctionExpr = createTypeExpr(type); + castExpr->Arguments.Add(expr); return castExpr; } diff --git a/source/slang/modifier-defs.h b/source/slang/modifier-defs.h index ddfb9c410..597d59d9a 100644 --- a/source/slang/modifier-defs.h +++ b/source/slang/modifier-defs.h @@ -315,3 +315,11 @@ END_SYNTAX_CLASS() SYNTAX_CLASS(TupleVarModifier, Modifier) FIELD_INIT(TupleFieldModifier*, tupleField, nullptr) END_SYNTAX_CLASS() + +// A modifier to indicate that a constructor/initializer can be used +// to perform implicit type conversion, and to specify the cost of +// the conversion, if applied. +SYNTAX_CLASS(ImplicitConversionModifier, Modifier) + // The conversion cost, used to rank conversions + FIELD(ConversionCost, cost) +END_SYNTAX_CLASS() diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp index e232880e9..7da309d29 100644 --- a/source/slang/parser.cpp +++ b/source/slang/parser.cpp @@ -3341,9 +3341,12 @@ namespace Slang { RefPtr<TypeCastExpr> tcexpr = new ExplicitCastExpr(); parser->FillPosition(tcexpr.Ptr()); - tcexpr->TargetType = parser->ParseTypeExp(); + tcexpr->FunctionExpr = parser->ParseType(); parser->ReadToken(TokenType::RParent); - tcexpr->Expression = parser->ParseExpression(Precedence::Multiplicative); // Note(tfoley): need to double-check this + + auto arg = parser->ParseExpression(Precedence::Multiplicative); // Note(tfoley): need to double-check this + tcexpr->Arguments.Add(arg); + return tcexpr; } else @@ -3913,7 +3916,19 @@ namespace Slang return modifier; } + static RefPtr<RefObject> parseImplicitConversionModifier(Parser* parser, void* /*userData*/) + { + RefPtr<ImplicitConversionModifier> modifier = new ImplicitConversionModifier(); + ConversionCost cost = kConversionCost_Default; + if( AdvanceIf(parser, TokenType::LParent) ) + { + cost = ConversionCost(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).Content)); + parser->ReadToken(TokenType::RParent); + } + modifier->cost = cost; + return modifier; + } RefPtr<ModuleDecl> populateBaseLanguageModule( Session* session, @@ -4005,6 +4020,7 @@ namespace Slang MODIFIER(__builtin_type, parseBuiltinTypeModifier); MODIFIER(__magic_type, parseMagicTypeModifier); + MODIFIER(__implicit_conversion, parseImplicitConversionModifier); #undef MODIFIER diff --git a/source/slang/slang-stdlib.cpp b/source/slang/slang-stdlib.cpp index 3ec8b9db4..89fcd0800 100644 --- a/source/slang/slang-stdlib.cpp +++ b/source/slang/slang-stdlib.cpp @@ -1066,6 +1066,13 @@ namespace Slang return stdlibPath; } + // We are going to generate the stdlib source code from a more compact + // description. For example, we need to generate all the `operator` + // declarations for the basic unary and binary math operations on + // builtin types. To do this, we will make a big array of all these + // types, and associate them with data on their categories/capabilities + // so that we generate only the correct operations. + // enum { SINT_MASK = 1 << 0, @@ -1082,21 +1089,142 @@ namespace Slang ANY_MASK = INT_MASK | FLOAT_MASK | BOOL_MASK, }; - static const struct { + // We are going to declare initializers that allow for conversion between + // all of our base types, and we need a way to priotize those conversion + // by giving them different costs. Rather than maintain a hard-coded table + // of N^2 costs for N basic types, we are going to try to do things a bit + // more systematically. + // + // Every base type will be given a "kind" and a "rank" for conversion. + // The kind will classify it as signed/unsigned/float, and the rank will + // classify it by its logical bit size (with a distinct rank for pointer-sized + // types that logically sits between 32- and 64-bit types). + // + enum BaseTypeConversionKind : uint8_t + { + kBaseTypeConversionKind_Signed, + kBaseTypeConversionKind_Unsigned, + kBaseTypeConversionKind_Float, + kBaseTypeConversionKind_Error, + }; + enum BaseTypeConversionRank : uint8_t + { + kBaseTypeConversionRank_Bool, + kBaseTypeConversionRank_Int8, + kBaseTypeConversionRank_Int16, + kBaseTypeConversionRank_Int32, + kBaseTypeConversionRank_IntPtr, + kBaseTypeConversionRank_Int64, + kBaseTypeConversionRank_Error, + }; + + // Here we declare the table of all our builtin types, so that we can generate all the relevant declarations. + // + struct BaseTypeInfo + { char const* name; BaseType tag; unsigned flags; - } kBaseTypes[] = { - { "void", BaseType::Void, 0 }, - { "int", BaseType::Int, SINT_MASK }, - { "half", BaseType::Half, FLOAT_MASK }, - { "float", BaseType::Float, FLOAT_MASK }, - { "double", BaseType::Double, FLOAT_MASK }, - { "uint", BaseType::UInt, UINT_MASK }, - { "bool", BaseType::Bool, BOOL_MASK }, - { "uint64_t", BaseType::UInt64, UINT_MASK }, + BaseTypeConversionKind conversionKind; + BaseTypeConversionRank conversionRank; + }; + static const BaseTypeInfo kBaseTypes[] = { + // TODO: `void` really shouldn't be in the `BaseType` enumeration, since it behaves so differently across the board + { "void", BaseType::Void, 0, kBaseTypeConversionKind_Error, kBaseTypeConversionRank_Error}, + + { "bool", BaseType::Bool, BOOL_MASK, kBaseTypeConversionKind_Unsigned, kBaseTypeConversionRank_Bool }, + + // TODO: should declare explicit types for 8-, 16-, 32- and 64-bit integers + { "int", BaseType::Int, SINT_MASK, kBaseTypeConversionKind_Signed, kBaseTypeConversionRank_Int32}, + + { "half", BaseType::Half, FLOAT_MASK, kBaseTypeConversionKind_Float, kBaseTypeConversionRank_Int16}, + { "float", BaseType::Float, FLOAT_MASK, kBaseTypeConversionKind_Float, kBaseTypeConversionRank_Int32}, + { "double", BaseType::Double, FLOAT_MASK, kBaseTypeConversionKind_Float, kBaseTypeConversionRank_Int64}, + + { "uint", BaseType::UInt, UINT_MASK, kBaseTypeConversionKind_Unsigned, kBaseTypeConversionRank_Int32}, + { "uint64_t", BaseType::UInt64, UINT_MASK, kBaseTypeConversionKind_Unsigned, kBaseTypeConversionRank_Int64}, }; + // Given two base types, we need to be able to compute the cost of converting between them. + ConversionCost getBaseTypeConversionCost( + BaseTypeInfo const& toInfo, + BaseTypeInfo const& fromInfo) + { + if(toInfo.conversionKind == fromInfo.conversionKind + && toInfo.conversionRank == fromInfo.conversionRank) + { + // Thse should represent the exact same type. + return kConversionCost_None; + } + + // Conversions within the same kind are easist to handle + if (toInfo.conversionKind == fromInfo.conversionKind) + { + // If we are converting to a "larger" type, then + // we are doing a lossless promotion, and otherwise + // we are doing a demotion. + if( toInfo.conversionRank > fromInfo.conversionRank) + return kConversionCost_RankPromotion; + else + return kConversionCost_GeneralConversion; + } + + // If we are converting from an unsigned integer type to + // a signed integer type that is guaranteed to be larger, + // then that is also a lossless promotion. + // + // There is one additional wrinkle here, which is that + // a conversion from a 32-bit unsigned integer to a + // "pointer-sized" signed integer should be treated + // as unsafe, because the pointer size might also be + // 32 bits. + // + // The same basic exemption applied when converting + // *from* a pointer-sized unsigned integer. + else if(toInfo.conversionKind == kBaseTypeConversionKind_Signed + && fromInfo.conversionKind == kBaseTypeConversionKind_Unsigned + && toInfo.conversionRank > fromInfo.conversionRank + && toInfo.conversionRank != kBaseTypeConversionRank_IntPtr + && fromInfo.conversionRank != kBaseTypeConversionRank_IntPtr) + { + return kConversionCost_UnsignedToSignedPromotion; + } + + // Conversion from signed to unsigned is always lossy, + // but it is preferred over conversions from unsigned + // to signed, for same-size types. + else if(toInfo.conversionKind == kBaseTypeConversionKind_Unsigned + && fromInfo.conversionKind == kBaseTypeConversionKind_Signed + && toInfo.conversionRank >= fromInfo.conversionRank) + { + return kConversionCost_SignedToUnsignedConversion; + } + + // Conversion from an integer to a floating-point type + // is never considered a promotion (even when the value + // would fit in the available mantissa bits). + // If the destination type is at least 32 bits we consider + // this a reasonably good conversion, though. + // + // Note that this means we do *not* consider implicit + // conversion to `half` as a good conversion, even for small + // types. This makes sense because we relaly want to prefer + // conversion to `float` as the default. + else if (toInfo.conversionKind == kBaseTypeConversionKind_Float + && toInfo.conversionRank >= kBaseTypeConversionRank_Int32) + { + return kConversionCost_IntegerToFloatConversion; + } + + // All other cases are considered as "general" conversions, + // where we don't consider any one conversion better than + // any others. + else + { + return kConversionCost_GeneralConversion; + } + } + struct OpInfo { IntrinsicOp opCode; char const* opName; unsigned flags; }; static const OpInfo unaryOps[] = { @@ -1147,7 +1275,6 @@ namespace Slang { IntrinsicOp::RshAssign, ">>=", ASSIGNMENT | INT_MASK }, }; - String Session::getCoreLibraryCode() { if (coreLibraryCode.Length() > 0) @@ -1201,9 +1328,20 @@ namespace Slang // Declare initializers to convert from various other types for (int ss = 0; ss < kBaseTypeCount; ++ss) { + // Don't allow conversion from `void` if (kBaseTypes[ss].tag == BaseType::Void) continue; + // We need to emit a modifier so that the semantic-checking + // layer will know it can use these operations for implicit + // conversion. + ConversionCost conversionCost = getBaseTypeConversionCost( + kBaseTypes[tt], + kBaseTypes[ss]); + + EMIT_LINE_DIRECTIVE(); + sb << "__implicit_conversion(" << conversionCost << ")\n"; + EMIT_LINE_DIRECTIVE(); sb << "__init(" << kBaseTypes[ss].name << " value);\n"; } @@ -1215,8 +1353,14 @@ namespace Slang sb << "__generic<T = float, let N : int = 4> __magic_type(Vector) struct vector\n{\n"; sb << " typedef T Element;\n"; - sb << " __init(T value);\n"; // initialize from single scalar + + // Declare initializer taking a single scalar of the elemnt type + sb << " __implicit_conversion(" << kConversionCost_ScalarToVector << ")\n"; + sb << " __init(T value);\n"; + sb << "};\n"; + + // TODO: Probably need to do similar sb << "__generic<T = float, let R : int = 4, let C : int = 4> __magic_type(Matrix) struct matrix {};\n"; static const struct { @@ -1306,6 +1450,39 @@ namespace Slang sb << "}\n"; } + // The above extension was generic in the *type* of the vector, + // but explicit in the *size*. We will now declare an extension + // for each builtin type that is generic in the size. + // + for (int tt = 0; tt < kBaseTypeCount; ++tt) + { + if(kBaseTypes[tt].tag == BaseType::Void) continue; + + sb << "__generic<let N : int> __extension vector<" + << kBaseTypes[tt].name << ",N>\n{\n"; + + for (int ff = 0; ff < kBaseTypeCount; ++ff) + { + if(kBaseTypes[ff].tag == BaseType::Void) continue; + + // We need a constructor to make a vector from a scalar + // of another type. + + if( tt != ff ) + { + auto cost = getBaseTypeConversionCost( + kBaseTypes[tt], + kBaseTypes[ff]); + cost += kConversionCost_ScalarToVector; + + sb << " __implicit_conversion(" << cost << ")\n"; + sb << " __init(" << kBaseTypes[ff].name << " value);\n"; + } + } + + sb << "}\n"; + } + for( int R = 2; R <= 4; ++R ) for( int C = 2; C <= 4; ++C ) { @@ -1337,6 +1514,14 @@ namespace Slang // with implicit conversion, in the `vector` case above sb << "__generic<U> __init(matrix<U," << R << ", " << C << ">);\n"; + // initialize from a matrix of larger size + for(int rr = R; rr <= 4; ++rr) + for( int cc = C; cc <= 4; ++cc ) + { + if(rr == R && cc == C) continue; + sb << "__init(matrix<T," << rr << "," << cc << "> value);\n"; + } + sb << "}\n"; } diff --git a/source/slang/syntax-base-defs.h b/source/slang/syntax-base-defs.h index e6f7cc55e..8a22a61d2 100644 --- a/source/slang/syntax-base-defs.h +++ b/source/slang/syntax-base-defs.h @@ -4,10 +4,16 @@ // AST nodes and related objects. For example, this is where the // basic `Decl`, `Stmt`, `Expr`, `type`, etc. definitions come from. +ABSTRACT_SYNTAX_CLASS(NodeBase, RefObject) + // A helper to access the corresponding class on a concrete instance + RAW( + virtual SyntaxClass<NodeBase> getClass() = 0; + ) +END_SYNTAX_CLASS() + // Base class for all nodes representing actual syntax // (thus having a location in the source code) -ABSTRACT_SYNTAX_CLASS(SyntaxNodeBase, RefObject) - +ABSTRACT_SYNTAX_CLASS(SyntaxNodeBase, NodeBase) // The primary source location associated with this AST node FIELD(SourceLoc, loc) @@ -26,7 +32,7 @@ END_SYNTAX_CLASS() // These are *not* syntax nodes, because they do not have // a unique location, and any two `Val`s representing // the same value should be conceptually equal. -ABSTRACT_SYNTAX_CLASS(Val, RefObject) +ABSTRACT_SYNTAX_CLASS(Val, NodeBase) RAW(typedef IValVisitor Visitor;) RAW(virtual void accept(IValVisitor* visitor, void* extra) = 0;) diff --git a/source/slang/syntax.cpp b/source/slang/syntax.cpp index be1a429a6..8bd42a7e4 100644 --- a/source/slang/syntax.cpp +++ b/source/slang/syntax.cpp @@ -69,6 +69,7 @@ namespace Slang void NAME::accept(NAME::Visitor* visitor, void* extra) \ { visitor->dispatch_##NAME(this, extra); } \ void* SyntaxClassBase::Impl<NAME>::createFunc() { return new NAME(); } \ + SyntaxClass<NodeBase> NAME::getClass() { return Slang::getClass<NAME>(); } \ SyntaxClassBase::ClassInfo const SyntaxClassBase::Impl<NAME>::kClassInfo = { #NAME, &SyntaxClassBase::Impl<BASE>::kClassInfo, &SyntaxClassBase::Impl<NAME>::createFunc }; #include "expr-defs.h" #include "decl-defs.h" @@ -79,13 +80,14 @@ namespace Slang SyntaxClassBase::ClassInfo const SyntaxClassBase::Impl<RefObject>::kClassInfo = { "RefObject", nullptr, nullptr }; -ABSTRACT_SYNTAX_CLASS(SyntaxNodeBase, RefObject); +ABSTRACT_SYNTAX_CLASS(NodeBase, RefObject); +ABSTRACT_SYNTAX_CLASS(SyntaxNodeBase, NodeBase); ABSTRACT_SYNTAX_CLASS(SyntaxNode, SyntaxNodeBase); ABSTRACT_SYNTAX_CLASS(ModifiableSyntaxNode, SyntaxNode); ABSTRACT_SYNTAX_CLASS(DeclBase, ModifiableSyntaxNode); ABSTRACT_SYNTAX_CLASS(Decl, DeclBase); ABSTRACT_SYNTAX_CLASS(Stmt, ModifiableSyntaxNode); -ABSTRACT_SYNTAX_CLASS(Val, RefObject); +ABSTRACT_SYNTAX_CLASS(Val, NodeBase); ABSTRACT_SYNTAX_CLASS(Type, Val); ABSTRACT_SYNTAX_CLASS(Modifier, SyntaxNodeBase); ABSTRACT_SYNTAX_CLASS(Expr, SyntaxNode); diff --git a/source/slang/syntax.h b/source/slang/syntax.h index e25f096ff..3a7d25f6c 100644 --- a/source/slang/syntax.h +++ b/source/slang/syntax.h @@ -30,6 +30,50 @@ namespace Slang typedef RefPtr<RefObject> (*SyntaxParseCallback)(Parser* parser, void* userData); + typedef unsigned int ConversionCost; + enum : ConversionCost + { + // No conversion at all + kConversionCost_None = 0, + + // Conversions based on explicit sub-typing relationships are the cheapest + // + // TODO(tfoley): We will eventually need a discipline for ranking + // when two up-casts are comparable. + kConversionCost_CastToInterface = 50, + + // Conversion that is lossless and keeps the "kind" of the value the same + kConversionCost_RankPromotion = 100, + + // Conversions that are lossless, but change "kind" + kConversionCost_UnsignedToSignedPromotion = 200, + + // Conversion from signed->unsigned integer of same or greater size + kConversionCost_SignedToUnsignedConversion = 300, + + // Cost of converting an integer to a floating-point type + kConversionCost_IntegerToFloatConversion = 400, + + // Default case (usable for user-defined conversions) + kConversionCost_Default = 500, + + // Catch-all for conversions that should be discouraged + // (i.e., that really shouldn't be made implicitly) + // + // TODO: make these conversions not be allowed implicitly in "Slang mode" + kConversionCost_GeneralConversion = 900, + + // This is the cost of an explicit conversion, which should + // not actually be performed. + kConversionCost_Explicit = 90000, + + // Additional conversion cost to add when promoting from a scalar to + // a vector (this will be added to the cost, if any, of converting + // the element type of the vector) + kConversionCost_ScalarToVector = 1, + }; + + // Forward-declare all syntax classes #define SYNTAX_CLASS(NAME, BASE, ...) class NAME; #include "object-meta-begin.h" @@ -836,6 +880,7 @@ namespace Slang #define SYNTAX_CLASS(NAME, BASE, ...) \ class NAME : public BASE { \ virtual void accept(NAME::Visitor* visitor, void* extra) override; \ + public: virtual SyntaxClass<NodeBase> getClass() override; \ public: /* ... */ #include "expr-defs.h" #include "decl-defs.h" |
