1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 03:28:20 +02:00

Support for operators with ref parameters, sized array->span

This commit is contained in:
Brian Fiete 2021-01-26 11:06:17 -08:00
parent 5c05b15b37
commit 1e52dce7c4
9 changed files with 183 additions and 31 deletions

View file

@ -20,11 +20,23 @@ namespace System
return val.mVal;
}
public implicit static operator Span<T> (ref Self val)
{
#unwarn
return .(&val.mVal, CSize);
}
public override void ToString(String strBuffer) mut
{
if (typeof(T) == typeof(char8))
{
strBuffer.Append((char8*)&mVal, CSize);
int len = 0;
for (; len < CSize; len++)
{
if (mVal[len] == default)
break;
}
strBuffer.Append((char8*)&mVal, len);
return;
}
@ -87,7 +99,7 @@ namespace System
get
{
return mIndex - 1;
}
}
}
public Result<T> GetNext() mut

View file

@ -177,6 +177,7 @@ void BfMethodMatcher::Init(/*SizedArrayImpl<BfResolvedArg>& arguments, */BfSized
mAllowNonStatic = true;
mSkipImplicitParams = false;
mAllowImplicitThis = false;
mAllowImplicitRef = false;
mHadVarConflictingReturnType = false;
mAutoFlushAmbiguityErrors = true;
mMethodCheckCount = 0;
@ -2066,8 +2067,13 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst
goto NoMatch;
}
else if (!mModule->CanCast(argTypedValue, wantType))
goto NoMatch;
else
{
if ((mAllowImplicitRef) && (wantType->IsRef()) && (!argTypedValue.mType->IsRef()))
wantType = wantType->GetUnderlyingType();
if (!mModule->CanCast(argTypedValue, wantType))
goto NoMatch;
}
}
paramIdx++;
@ -5964,7 +5970,10 @@ BfTypedValue BfExprEvaluator::CreateCall(BfMethodMatcher* methodMatcher, BfTyped
PerformCallChecks(moduleMethodInstance.mMethodInstance, methodMatcher->mTargetSrc);
return CreateCall(methodMatcher->mTargetSrc, target, BfTypedValue(), methodMatcher->mBestMethodDef, moduleMethodInstance, false, methodMatcher->mArguments);
BfCreateFallFlags callFlags = BfCreateFallFlags_None;
if (methodMatcher->mAllowImplicitRef)
callFlags = (BfCreateFallFlags)(callFlags | BfCreateFallFlags_AllowImplicitRef);
return CreateCall(methodMatcher->mTargetSrc, target, BfTypedValue(), methodMatcher->mBestMethodDef, moduleMethodInstance, callFlags, methodMatcher->mArguments);
}
void BfExprEvaluator::MakeBaseConcrete(BfTypedValue& typedValue)
@ -6295,8 +6304,11 @@ void BfExprEvaluator::AddCallDependencies(BfMethodInstance* methodInstance)
}
//TODO: delete argumentsZ
BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValue& inTarget, const BfTypedValue& origTarget, BfMethodDef* methodDef, BfModuleMethodInstance moduleMethodInstance, bool bypassVirtual, SizedArrayImpl<BfResolvedArg>& argValues, BfTypedValue* argCascade, bool skipThis)
BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValue& inTarget, const BfTypedValue& origTarget, BfMethodDef* methodDef, BfModuleMethodInstance moduleMethodInstance, BfCreateFallFlags callFlags, SizedArrayImpl<BfResolvedArg>& argValues, BfTypedValue* argCascade)
{
bool bypassVirtual = (callFlags & BfCreateFallFlags_BypassVirtual) != 0;
bool skipThis = (callFlags & BfCreateFallFlags_SkipThis) != 0;;
static int sCallIdx = 0;
if (!mModule->mCompiler->mIsResolveOnly)
sCallIdx++;
@ -6999,6 +7011,10 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
if (refNode == NULL)
refNode = targetSrc;
if (((callFlags & BfCreateFallFlags_AllowImplicitRef) != 0) &&
(wantType->IsRef()) && (!argValue.mType->IsRef()))
argValue = mModule->ToRef(argValue, (BfRefType*)wantType);
if (mModule->mCurMethodState != NULL)
{
SetAndRestoreValue<BfScopeData*> prevScopeData(mModule->mCurMethodState->mOverrideScope, boxScopeData);
@ -7413,7 +7429,7 @@ BfTypedValue BfExprEvaluator::MatchConstructor(BfAstNode* targetSrc, BfMethodBou
if (isFailurePass)
mModule->Fail(StrFormat("'%s' is inaccessible due to its protection level", mModule->MethodToString(moduleMethodInstance.mMethodInstance).c_str()), targetSrc);
prevBindResult.Restore();
return CreateCall(methodMatcher.mTargetSrc, target, BfTypedValue(), methodMatcher.mBestMethodDef, moduleMethodInstance, false, methodMatcher.mArguments);
return CreateCall(methodMatcher.mTargetSrc, target, BfTypedValue(), methodMatcher.mBestMethodDef, moduleMethodInstance, BfCreateFallFlags_None, methodMatcher.mArguments);
}
static int sInvocationIdx = 0;
@ -8987,7 +9003,12 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp
mModule->Fail(StrFormat("Method '%s' can only be invoked at comptime. Consider adding [Comptime] to the current method.", mModule->MethodToString(moduleMethodInstance.mMethodInstance).c_str()), targetSrc);
}
result = CreateCall(targetSrc, callTarget, origTarget, methodDef, moduleMethodInstance, bypassVirtual, argValues.mResolvedArgs, &argCascade, skipThis);
BfCreateFallFlags subCallFlags = BfCreateFallFlags_None;
if (bypassVirtual)
subCallFlags = (BfCreateFallFlags)(subCallFlags | BfCreateFallFlags_BypassVirtual);
if (skipThis)
subCallFlags = (BfCreateFallFlags)(subCallFlags | BfCreateFallFlags_SkipThis);
result = CreateCall(targetSrc, callTarget, origTarget, methodDef, moduleMethodInstance, subCallFlags, argValues.mResolvedArgs, &argCascade);
}
if (overrideReturnType != NULL)
@ -19429,6 +19450,7 @@ BfTypedValue BfExprEvaluator::PerformUnaryOperation_TryOperator(const BfTypedVal
args.push_back(resolvedArg);
BfMethodMatcher methodMatcher(opToken, mModule, "", args, NULL);
methodMatcher.mBfEvalExprFlags = BfEvalExprFlags_NoAutoComplete;
methodMatcher.mAllowImplicitRef = true;
BfBaseClassWalker baseClassWalker(inValue.mType, NULL, mModule);
BfUnaryOp findOp = unaryOp;
@ -20885,9 +20907,13 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod
args.push_back(leftArg);
}
auto checkLeftType = leftValue.mType;
auto checkRightType = rightValue.mType;
BfMethodMatcher methodMatcher(opToken, mModule, "", args, NULL);
methodMatcher.mAllowImplicitRef = true;
methodMatcher.mBfEvalExprFlags = BfEvalExprFlags_NoAutoComplete;
BfBaseClassWalker baseClassWalker(leftValue.mType, rightValue.mType, mModule);
BfBaseClassWalker baseClassWalker(checkLeftType, checkRightType, mModule);
bool invertResult = false;
BfType* operatorConstraintReturnType = NULL;
@ -21461,7 +21487,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod
argValues.push_back(resolvedArg);
resolvedArg.mTypedValue = rightValue;
argValues.push_back(resolvedArg);
mResult = CreateCall(opToken, BfTypedValue(), BfTypedValue(), moduleMethodInstance.mMethodInstance->mMethodDef, moduleMethodInstance, false, argValues);
mResult = CreateCall(opToken, BfTypedValue(), BfTypedValue(), moduleMethodInstance.mMethodInstance->mMethodDef, moduleMethodInstance, BfCreateFallFlags_None, argValues);
if ((mResult) &&
((binaryOp == BfBinaryOp_InEquality) || (binaryOp == BfBinaryOp_StrictInEquality)))
mResult.mValue = mModule->mBfIRBuilder->CreateNot(mResult.mValue);

View file

@ -41,6 +41,14 @@ enum BfResolveArgFlags
BfResolveArgFlag_FromGenericParam = 2
};
enum BfCreateFallFlags
{
BfCreateFallFlags_None,
BfCreateFallFlags_BypassVirtual = 1,
BfCreateFallFlags_SkipThis = 2,
BfCreateFallFlags_AllowImplicitRef = 4
};
class BfResolvedArg
{
public:
@ -186,10 +194,11 @@ public:
bool mHadVarConflictingReturnType;
bool mBypassVirtual;
bool mAllowImplicitThis;
bool mAllowImplicitRef;
bool mAllowStatic;
bool mAllowNonStatic;
bool mSkipImplicitParams;
bool mAutoFlushAmbiguityErrors;
bool mAutoFlushAmbiguityErrors;
BfEvalExprFlags mBfEvalExprFlags;
int mMethodCheckCount;
BfType* mExplicitInterfaceCheck;
@ -435,7 +444,7 @@ public:
void AddCallDependencies(BfMethodInstance* methodInstance);
void PerformCallChecks(BfMethodInstance* methodInstance, BfAstNode* targetSrc);
BfTypedValue CreateCall(BfAstNode* targetSrc, BfMethodInstance* methodInstance, BfIRValue func, bool bypassVirtual, SizedArrayImpl<BfIRValue>& irArgs, BfTypedValue* sret = NULL, bool isTailCall = false);
BfTypedValue CreateCall(BfAstNode* targetSrc, const BfTypedValue& target, const BfTypedValue& origTarget, BfMethodDef* methodDef, BfModuleMethodInstance methodInstance, bool bypassVirtual, SizedArrayImpl<BfResolvedArg>& argValues, BfTypedValue* argCascade = NULL, bool skipThis = false);
BfTypedValue CreateCall(BfAstNode* targetSrc, const BfTypedValue& target, const BfTypedValue& origTarget, BfMethodDef* methodDef, BfModuleMethodInstance methodInstance, BfCreateFallFlags callFlags, SizedArrayImpl<BfResolvedArg>& argValues, BfTypedValue* argCascade = NULL);
BfTypedValue CreateCall(BfMethodMatcher* methodMatcher, BfTypedValue target);
void MakeBaseConcrete(BfTypedValue& typedValue);
void SplatArgs(BfTypedValue value, SizedArrayImpl<BfIRValue>& irArgs);

View file

@ -11512,6 +11512,23 @@ BfTypedValue BfModule::RemoveRef(BfTypedValue typedValue)
return typedValue;
}
BfTypedValue BfModule::ToRef(BfTypedValue typedValue, BfRefType* refType)
{
if (refType == NULL)
refType = CreateRefType(typedValue.mType);
if ((refType->mRefKind == BfRefType::RefKind_Mut) && (typedValue.mType->IsObjectOrInterface()))
{
return LoadValue(typedValue);
}
if (refType->mRefKind == BfRefType::RefKind_Mut)
refType = CreateRefType(typedValue.mType);
typedValue = MakeAddressable(typedValue);
return BfTypedValue(typedValue.mValue, refType);
}
BfTypedValue BfModule::LoadValue(BfTypedValue typedValue, BfAstNode* refNode, bool isVolatile)
{
if (!typedValue.IsAddr())
@ -18299,10 +18316,17 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup)
Fail("Binary operators must declare two parameters", paramErrorRefNode);
}
else
{
if ((mCurMethodInstance->GetParamType(0) != mCurTypeInstance) && (!mCurMethodInstance->GetParamType(0)->IsSelf()) &&
(mCurMethodInstance->GetParamType(1) != mCurTypeInstance) && (!mCurMethodInstance->GetParamType(1)->IsSelf()))
{
{
auto checkParam0 = mCurMethodInstance->GetParamType(0);
if ((checkParam0->IsRef()) && (!checkParam0->IsOut()))
checkParam0 = checkParam0->GetUnderlyingType();
auto checkParam1 = mCurMethodInstance->GetParamType(1);
if ((checkParam1->IsRef()) && (!checkParam1->IsOut()))
checkParam1 = checkParam1->GetUnderlyingType();
if ((checkParam0 != mCurTypeInstance) && (!checkParam0->IsSelf()) &&
(checkParam1 != mCurTypeInstance) && (!checkParam1->IsSelf()))
{
Fail("At least one of the parameters of a binary operator must be the containing type", paramErrorRefNode);
}
}
@ -18321,11 +18345,15 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup)
{
if (methodDef->mIsStatic)
{
auto checkParam0 = mCurMethodInstance->GetParamType(0);
if ((checkParam0->IsRef()) && (!checkParam0->IsOut()))
checkParam0 = checkParam0->GetUnderlyingType();
if (methodDef->mParams.size() != 1)
{
Fail("Unary operators must declare one parameter", paramErrorRefNode);
}
else if ((mCurMethodInstance->GetParamType(0) != mCurTypeInstance) && (!mCurMethodInstance->GetParamType(0)->IsSelf()))
else if ((checkParam0 != mCurTypeInstance) && (!checkParam0->IsSelf()))
{
Fail("The parameter of a unary operator must be the containing type", paramErrorRefNode);
}
@ -18379,18 +18407,22 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup)
}
else
{
if ((mCurMethodInstance->GetParamType(0) != mCurTypeInstance) && (!mCurMethodInstance->GetParamType(0)->IsSelf()) &&
auto checkParam0 = mCurMethodInstance->GetParamType(0);
if ((checkParam0->IsRef()) && (!checkParam0->IsOut()))
checkParam0 = checkParam0->GetUnderlyingType();
if ((checkParam0 != mCurTypeInstance) && (!checkParam0->IsSelf()) &&
(mCurMethodInstance->mReturnType != mCurTypeInstance) && (!mCurMethodInstance->mReturnType->IsSelf()))
Fail("User-defined conversion must convert to or from the enclosing type", paramErrorRefNode);
if (mCurMethodInstance->GetParamType(0) == mCurMethodInstance->mReturnType)
if (checkParam0 == mCurMethodInstance->mReturnType)
Fail("User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type", operatorDef->mOperatorDeclaration->mReturnType);
// On type lookup error we default to 'object', so don't do the 'base class' error if that may have
// happened here
if ((!mHadBuildError) || ((mCurMethodInstance->mReturnType != mContext->mBfObjectType) && (mCurMethodInstance->GetParamType(0) != mContext->mBfObjectType)))
if ((!mHadBuildError) || ((mCurMethodInstance->mReturnType != mContext->mBfObjectType) && (checkParam0 != mContext->mBfObjectType)))
{
auto isToBase = TypeIsSubTypeOf(mCurTypeInstance->mBaseType, mCurMethodInstance->mReturnType->ToTypeInstance());
bool isFromBase = TypeIsSubTypeOf(mCurTypeInstance->mBaseType, mCurMethodInstance->GetParamType(0)->ToTypeInstance());
bool isFromBase = TypeIsSubTypeOf(mCurTypeInstance->mBaseType, checkParam0->ToTypeInstance());
if ((mCurTypeInstance->IsObject()) && (isToBase || isFromBase))
Fail("User-defined conversions to or from a base class are not allowed", paramErrorRefNode);
@ -22218,16 +22250,22 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool
methodDef->mIsVirtual = false;
}
if ((methodDeclaration != NULL) && (methodDeclaration->mMutSpecifier != NULL) && (!mCurTypeInstance->IsBoxed()) && (!methodInstance->mIsForeignMethodDef))
BfAstNode* mutSpecifier = NULL;
if (methodDeclaration != NULL)
mutSpecifier = methodDeclaration->mMutSpecifier;
else if (methodDef->GetPropertyMethodDeclaration() != NULL)
mutSpecifier = methodDef->GetPropertyMethodDeclaration()->mMutSpecifier;
if ((mutSpecifier != NULL) && (!mCurTypeInstance->IsBoxed()) && (!methodInstance->mIsForeignMethodDef))
{
if (methodDef->mIsStatic)
Warn(0, "Unnecessary 'mut' specifier, static methods have no implicit 'this' target to mutate", methodDeclaration->mMutSpecifier);
Warn(0, "Unnecessary 'mut' specifier, static methods have no implicit 'this' target to mutate", mutSpecifier);
else if ((!mCurTypeInstance->IsValueType()) && (!mCurTypeInstance->IsInterface()))
Warn(0, "Unnecessary 'mut' specifier, methods of reference types are implicitly mutating", methodDeclaration->mMutSpecifier);
Warn(0, "Unnecessary 'mut' specifier, methods of reference types are implicitly mutating", mutSpecifier);
else if (methodDef->mMethodType == BfMethodType_Ctor)
Warn(0, "Unnecessary 'mut' specifier, constructors are implicitly mutating", methodDeclaration->mMutSpecifier);
Warn(0, "Unnecessary 'mut' specifier, constructors are implicitly mutating", mutSpecifier);
else if (methodDef->mMethodType == BfMethodType_Dtor)
Warn(0, "Unnecessary 'mut' specifier, destructors are implicitly mutating", methodDeclaration->mMutSpecifier);
Warn(0, "Unnecessary 'mut' specifier, destructors are implicitly mutating", mutSpecifier);
}
if (isTemporaryFunc)

View file

@ -1627,6 +1627,7 @@ public:
void EmitDynamicCastCheck(BfTypedValue typedVal, BfType* type, bool allowNull);
void CheckStaticAccess(BfTypeInstance* typeInstance);
BfTypedValue RemoveRef(BfTypedValue typedValue);
BfTypedValue ToRef(BfTypedValue typedValue, BfRefType* refType = NULL);
BfTypedValue LoadOrAggregateValue(BfTypedValue typedValue);
BfTypedValue LoadValue(BfTypedValue typedValue, BfAstNode* refNode = NULL, bool isVolatile = false);
BfTypedValue PrepareConst(BfTypedValue& typedValue);

View file

@ -11597,7 +11597,7 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp
// Check user-defined operators
if ((castFlags & BfCastFlags_NoConversionOperator) == 0)
{
{
auto fromType = typedVal.mType;
auto fromTypeInstance = typedVal.mType->ToTypeInstance();
auto toTypeInstance = toType->ToTypeInstance();
@ -11620,7 +11620,7 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp
BfMethodInstance* opMethodInstance = NULL;
BfType* opMethodSrcType = NULL;
BfOperatorInfo* constraintOperatorInfo = NULL;
// Normal, lifted, execute
for (int pass = 0; pass < 3; pass++)
{
@ -11643,8 +11643,12 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp
break;
}
BfType* searchFromType = checkFromType;
if (searchFromType->IsSizedArray())
searchFromType = GetWrappedStructType(checkFromType);
bool isConstraintCheck = ((castFlags & BfCastFlags_IsConstraintCheck) != 0);
BfBaseClassWalker baseClassWalker(fromType, toType, this);
BfBaseClassWalker baseClassWalker(searchFromType, toType, this);
while (true)
{
@ -11687,6 +11691,9 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp
methodToType = methodInst->mReturnType;
}
if (methodFromType->IsRef())
methodFromType = methodFromType->GetUnderlyingType();
if (methodFromType->IsSelf())
methodFromType = entry.mSrcType;
if (methodToType->IsSelf())
@ -11708,6 +11715,11 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp
}
int fromDist = GetTypeDistance(methodCheckFromType, checkFromType);
if ((fromDist == INT_MAX) && (searchFromType != checkFromType))
{
fromDist = GetTypeDistance(methodCheckFromType, searchFromType);
}
if (fromDist < 0)
{
// Allow us to cast a constant int to a smaller type if it satisfies the cast operator
@ -11842,10 +11854,19 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp
}
}
}
// Actually perform conversion
BfExprEvaluator exprEvaluator(this);
auto castedFromValue = Cast(srcNode, typedVal, bestFromType, castFlags);
BfTypedValue castedFromValue;
if ((typedVal.mType->IsSizedArray()) && (bestFromType->IsInstanceOf(mCompiler->mSizedArrayTypeDef)))
{
castedFromValue = MakeAddressable(typedVal);
if (!bestFromType->IsValuelessType())
castedFromValue.mValue = mBfIRBuilder->CreateBitCast(castedFromValue.mValue, mBfIRBuilder->MapTypeInstPtr(bestFromType->ToTypeInstance()));
castedFromValue.mType = bestFromType;
}
else
castedFromValue = Cast(srcNode, typedVal, bestFromType, castFlags);
if (!castedFromValue)
return BfIRValue();
@ -11864,6 +11885,9 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp
BfModuleMethodInstance moduleMethodInstance = GetMethodInstance(opMethodInstance->GetOwner(), opMethodInstance->mMethodDef, BfTypeVector());
exprEvaluator.PerformCallChecks(moduleMethodInstance.mMethodInstance, srcNode);
if (moduleMethodInstance.mMethodInstance->GetParamType(0)->IsRef())
castedFromValue = ToRef(castedFromValue);
SizedArray<BfIRValue, 1> args;
exprEvaluator.PushArg(castedFromValue, args);
operatorOut = exprEvaluator.CreateCall(NULL, moduleMethodInstance.mMethodInstance, IsSkippingExtraResolveChecks() ? BfIRValue() : moduleMethodInstance.mFunc, false, args);

View file

@ -568,6 +568,8 @@ public:
virtual bool IsAllocType() { return false; }
virtual bool IsIntPtrable() { return false; }
virtual bool IsRef() { return false; }
virtual bool IsMut() { return false; }
virtual bool IsOut() { return false; }
virtual bool IsGenericParam() { return false; }
virtual bool IsClosure() { return false; }
virtual bool IsMethodRef() { return false; }
@ -2280,6 +2282,8 @@ public:
virtual bool IsReified() override { return mElementType->IsReified(); }
virtual bool IsRef() override { return true; }
virtual bool IsMut() override { return mRefKind == RefKind_Mut; }
virtual bool IsOut() override { return mRefKind == RefKind_Out; }
virtual bool IsDependentOnUnderlyingType() override { return true; }
virtual BfType* GetUnderlyingType() override { return mElementType; }
virtual bool IsUnspecializedType() override { return mElementType->IsUnspecializedType(); }

View file

@ -176,6 +176,28 @@ namespace Tests
}
}
struct StructG : this(int a)
{
public static StructG operator+(ref StructG lhs, ref StructG rhs)
{
lhs.a += 1000;
rhs.a += 1000;
return .(lhs.a + rhs.a);
}
public static StructG operator-(ref StructG val)
{
val.a += 1000;
return val;
}
public static implicit operator int(ref StructG val)
{
return val.a;
}
}
struct StructOp<T, T2> where T : operator T + T2
{
public T DoIt(T val, T2 val2)
@ -407,6 +429,15 @@ namespace Tests
Test.Assert(sf + 1.0f == 3);
Test.Assert(2.0f + sf == 3);
StructG sg = .(100);
StructG sg2 = .(200);
var sg3 = sg + sg2;
var sg4 = -sg3;
Test.Assert(sg.a == 1100);
Test.Assert(sg2.a == 1200);
Test.Assert(sg3.a == 3300);
Test.Assert(sg4.a == 3300);
/*let oai = OuterOp<float>.InnerOp<int>.Op(1.0f, 100);
Test.Assert(oai == 101.0f);
@ -443,6 +474,8 @@ namespace Tests
}
}
[Test]
public static void TestCompareWithCastOperator()
{
@ -504,3 +537,4 @@ namespace Tests
}
}
}

View file

@ -52,6 +52,10 @@ namespace Tests
ClassA[2] caArr0 = .(ca0, ca0);
ClassA[2] caArr1 = .(ca1, ca1);
Test.Assert(caArr0 == caArr1);
Span<int> span = val1;
Test.Assert(span[0] == 7);
Test.Assert(span[1] == 8);
}
[Test]