diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index e14ccbd7..ea119357 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -400,6 +400,7 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly) mGenericIRefEnumeratorTypeDef = NULL; mInlineAttributeTypeDef = NULL; mInternalTypeDef = NULL; + mIDisposableTypeDef = NULL; mIPrintableTypeDef = NULL; mIHashableTypeDef = NULL; mLinkNameAttributeTypeDef = NULL; @@ -5908,6 +5909,7 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) mGenericIRefEnumeratorTypeDef = _GetRequiredType("System.Collections.Generic.IRefEnumerator"); mInlineAttributeTypeDef = _GetRequiredType("System.InlineAttribute"); mInternalTypeDef = _GetRequiredType("System.Internal"); + mIDisposableTypeDef = _GetRequiredType("System.IDisposable"); mIPrintableTypeDef = _GetRequiredType("System.IPrintable"); mIHashableTypeDef = _GetRequiredType("System.IHashable"); mLinkNameAttributeTypeDef = _GetRequiredType("System.LinkNameAttribute"); diff --git a/IDEHelper/Compiler/BfCompiler.h b/IDEHelper/Compiler/BfCompiler.h index ae6c29f2..f5d8f84c 100644 --- a/IDEHelper/Compiler/BfCompiler.h +++ b/IDEHelper/Compiler/BfCompiler.h @@ -351,6 +351,7 @@ public: BfTypeDef* mGenericIRefEnumeratorTypeDef; BfTypeDef* mInternalTypeDef; + BfTypeDef* mIDisposableTypeDef; BfTypeDef* mIPrintableTypeDef; BfTypeDef* mIHashableTypeDef; diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 4324acae..1e63dc36 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -4964,7 +4964,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu } if ((mModule->mAttributeState != NULL) && (mModule->mAttributeState->mCustomAttributes != NULL) && (mModule->mAttributeState->mCustomAttributes->Contains(mModule->mCompiler->mDisableObjectAccessChecksAttributeTypeDef))) doAccessCheck = false; - if ((doAccessCheck) && (!isSkipCall)) + if ((doAccessCheck) && (!isSkipCall) && (prevBindResult.mPrevVal == NULL)) mModule->EmitObjectAccessCheck(target); } @@ -8292,6 +8292,19 @@ void BfExprEvaluator::Visit(BfDynamicCastExpression* dynCastExpr) } auto boolType = mModule->GetPrimitiveType(BfTypeCode_Boolean); + + if ((targetValue.mType->IsNullable()) && (targetType->IsInterface())) + { + mResult = mModule->Cast(dynCastExpr, targetValue, targetType, BfCastFlags_SilentFail); + if (!mResult) + { + mModule->Warn(0, StrFormat("Conversion from '%s' to '%s' will always be null", + mModule->TypeToString(targetValue.mType).c_str(), mModule->TypeToString(targetType).c_str()), dynCastExpr->mAsToken); + mResult = BfTypedValue(mModule->GetDefaultValue(targetType), targetType); + } + return; + } + if (targetValue.mType->IsValueTypeOrValueTypePtr()) { mModule->Warn(0, StrFormat("Type '%s' is not applicable for dynamic casting", diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 7a307b7e..0dc6838e 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -2688,16 +2688,13 @@ BfError* BfModule::Warn(int warningNum, const StringImpl& warning, BfAstNode* re // We used to bubble up warnings into the mixin injection site, BUT // we are now assuming any relevant warnings will be given at the declaration site return NULL; -// AddFailType(mCurTypeInstance); -// -// BfError* bfError = mCompiler->mPassInstance->Warn(warningNum, "Warning while injecting mixin", mCurMethodState->mMixinState->GetRoot()->mSource); -// if (bfError == NULL) -// return NULL; -// mHadBuildWarning = true; -// return mCompiler->mPassInstance->MoreInfo(warning, refNode); } - BfError* bfError = mCompiler->mPassInstance->WarnAt(warningNum, warning, refNode->GetSourceData(), refNode->GetSrcStart(), refNode->GetSrcLength()); + BfError* bfError; + if (refNode != NULL) + bfError = mCompiler->mPassInstance->WarnAt(warningNum, warning, refNode->GetSourceData(), refNode->GetSrcStart(), refNode->GetSrcLength()); + else + bfError = mCompiler->mPassInstance->Warn(warningNum, warning); if (bfError != NULL) { bfError->mProject = mProject; diff --git a/IDEHelper/Compiler/BfStmtEvaluator.cpp b/IDEHelper/Compiler/BfStmtEvaluator.cpp index a1d2a935..f81ade7d 100644 --- a/IDEHelper/Compiler/BfStmtEvaluator.cpp +++ b/IDEHelper/Compiler/BfStmtEvaluator.cpp @@ -517,7 +517,7 @@ void BfModule::AddDeferredBlock(BfBlock* block, BfScopeData* scopeData, ArraymDeferredBlock = block; if (captures != NULL) deferredCallEntry->mCaptures = *captures; - AddDeferredCallEntry(deferredCallEntry, scopeData); \ + AddDeferredCallEntry(deferredCallEntry, scopeData); } BfDeferredCallEntry* BfModule::AddDeferredCall(const BfModuleMethodInstance& moduleMethodInstance, SizedArrayImpl& llvmArgs, BfScopeData* scopeData, BfAstNode* srcNode, bool bypassVirtual, bool doNullCheck) @@ -5014,15 +5014,39 @@ void BfModule::Visit(BfUsingStatement* usingStmt) } if (!failed) - { - exprEvaluator.mFunctionBindResult = &functionBindResult; - BfResolvedArgs resolvedArgs; - exprEvaluator.MatchMethod(usingStmt->mVariableDeclaration, NULL, embeddedValue, false, false, "Dispose", resolvedArgs, NULL); - if (functionBindResult.mMethodInstance != NULL) + { + auto iDisposableType = ResolveTypeDef(mCompiler->mIDisposableTypeDef)->ToTypeInstance(); + auto dispMethod = GetMethodByName(iDisposableType, "Dispose"); + + if ((!dispMethod) || (!CanCast(embeddedValue, iDisposableType))) { - moduleMethodInstance = BfModuleMethodInstance(functionBindResult.mMethodInstance, functionBindResult.mFunc); - AddDeferredCall(moduleMethodInstance, functionBindResult.mIRArgs, mCurMethodState->mCurScope); - } + Fail(StrFormat("Type '%s' must be implicitly convertible to 'System.IDisposable' for use in 'using' statement", TypeToString(embeddedValue.mType).c_str()), usingStmt->mVariableDeclaration); + failed = true; + } + else + { + bool mayBeNull = true; + if (embeddedValue.mType->IsStruct()) + { + // It's possible that a struct can convert to an IDisposable through a conversion operator that CAN + // return null, so the only way we can know we are not null is if we are a struct that directly + // implements the interface + if (TypeIsSubTypeOf(embeddedValue.mType->ToTypeInstance(), iDisposableType)) + mayBeNull = false; + } + + exprEvaluator.mFunctionBindResult = &functionBindResult; + SizedArray resolvedArgs; + BfMethodMatcher methodMatcher(usingStmt->mVariableDeclaration, this, dispMethod.mMethodInstance, resolvedArgs); + methodMatcher.CheckType(iDisposableType, embeddedValue, false); + methodMatcher.TryDevirtualizeCall(embeddedValue); + auto retVal = exprEvaluator.CreateCall(&methodMatcher, embeddedValue); + if (functionBindResult.mMethodInstance != NULL) + { + moduleMethodInstance = BfModuleMethodInstance(functionBindResult.mMethodInstance, functionBindResult.mFunc); + AddDeferredCall(moduleMethodInstance, functionBindResult.mIRArgs, mCurMethodState->mCurScope, NULL, false, mayBeNull); + } + } } if (usingStmt->mEmbeddedStatement == NULL)