summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2017-09-05 13:16:21 -0700
committerGitHub <noreply@github.com>2017-09-05 13:16:21 -0700
commite59a1b39317c10815baaed3b70c4a6580dbe1e63 (patch)
treedc908e481eb5d73ceb7dc9d1f5da1e40d5b0e7e8 /source
parent620734080f825cb205b887fa1d6203e35dd60663 (diff)
parent8b91fb8e2db782c73541c83042209b4263402902 (diff)
Merge pull request #175 from tfoleyNV/implicit-conversion
Move implicit conversion operations to stdlib
Diffstat (limited to 'source')
-rw-r--r--source/slang/check.cpp454
-rw-r--r--source/slang/emit.cpp96
-rw-r--r--source/slang/expr-defs.h6
-rw-r--r--source/slang/lower.cpp42
-rw-r--r--source/slang/modifier-defs.h8
-rw-r--r--source/slang/parser.cpp20
-rw-r--r--source/slang/slang-stdlib.cpp209
-rw-r--r--source/slang/syntax-base-defs.h12
-rw-r--r--source/slang/syntax.cpp6
-rw-r--r--source/slang/syntax.h45
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"