diff --git a/IDE/Tests/CompileFail001/src/Generics.bf b/IDE/Tests/CompileFail001/src/Generics.bf index 447928b4..d0ac2f3f 100644 --- a/IDE/Tests/CompileFail001/src/Generics.bf +++ b/IDE/Tests/CompileFail001/src/Generics.bf @@ -119,6 +119,38 @@ namespace IDETest { } + + public class TestExt where T : struct + { + public struct InnerA + { + + } + + public struct InnerB where T2 : struct + { + + } + } + + extension TestExt + where T : Int + { + public int a = 0; + + public struct InnerC + { + } + } + + static void TestExtMethod() + { + TestExt.InnerA a; //FAIL + TestExt.InnerB b; //FAIL + TestExt.InnerB c; + TestExt.InnerC d; + TestExt.InnerC e; //FAIL + } } } diff --git a/IDEHelper/Compiler/BfAutoComplete.cpp b/IDEHelper/Compiler/BfAutoComplete.cpp index 9692ff8f..d9389499 100644 --- a/IDEHelper/Compiler/BfAutoComplete.cpp +++ b/IDEHelper/Compiler/BfAutoComplete.cpp @@ -713,15 +713,27 @@ const char* BfAutoComplete::GetTypeName(BfType* type) } void BfAutoComplete::AddInnerTypes(BfTypeInstance* typeInst, const StringImpl& filter, bool allowProtected, bool allowPrivate) -{ +{ if (typeInst->IsEnum()) AddEntry(AutoCompleteEntry("valuetype", "UnderlyingType"), filter); for (auto innerType : typeInst->mTypeDef->mNestedTypes) { + if (innerType->mOuterType->mTypeCode == BfTypeCode_Extension) + { + if (typeInst->mDefineState < BfTypeDefineState_Defined) + mModule->PopulateType(typeInst); + + if ((typeInst->mGenericTypeInfo != NULL) && (typeInst->mGenericTypeInfo->mGenericExtensionInfo != NULL)) + { + if (!typeInst->mGenericTypeInfo->mGenericExtensionInfo->mConstraintsPassedSet.IsSet(innerType->mOuterType->mPartialIdx)) + continue; + } + } + if (CheckProtection(innerType->mProtection, innerType, allowProtected, allowPrivate)) AddTypeDef(innerType, filter); - } + } allowPrivate = false; if (typeInst->mBaseType != NULL) diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index 49d68b01..784b9735 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -9423,14 +9423,31 @@ BfType* BfModule::ResolveTypeResult(BfTypeReference* typeRef, BfType* resolvedTy populateModule->PopulateType(resolvedTypeRef, populateType); if ((typeInstance != NULL) && (typeInstance->mTypeDef != NULL) && (typeInstance->mTypeDef->mProtection == BfProtection_Internal) && - (typeInstance != mCurTypeInstance) && (typeInstance->mTypeDef->mOuterType == NULL) && (!typeRef->IsTemporary())) + (typeInstance != mCurTypeInstance) && (typeInstance->mTypeDef->mOuterType == NULL) && (!typeRef->IsTemporary())) { if (!CheckProtection(typeInstance->mTypeDef->mProtection, typeInstance->mTypeDef, false, false)) Fail(StrFormat("'%s' is inaccessible due to its protection level", TypeToString(typeInstance).c_str()), typeRef); // CS0122 } - if ((populateType > BfPopulateType_IdentityNoRemapAlias) && (!ResolveTypeResult_Validate(typeRef, resolvedTypeRef))) - return NULL; + // If the inner type is definted in an extension then we need to make sure the constraints are good + if ((typeInstance != NULL) && (typeInstance->mTypeDef != NULL) && (typeInstance->mTypeDef->mOuterType != NULL) && + (typeInstance->mTypeDef->mOuterType->mTypeCode == BfTypeCode_Extension)) + { + auto outerType = GetOuterType(typeInstance); + if ((outerType->mGenericTypeInfo != NULL) && (outerType->mGenericTypeInfo->mGenericExtensionInfo != NULL)) + { + if (!outerType->mGenericTypeInfo->mGenericExtensionInfo->mConstraintsPassedSet.IsSet(typeInstance->mTypeDef->mOuterType->mPartialIdx)) + { + Fail(StrFormat("'%s' is declared inside a type extension whose constraints were not met", TypeToString(typeInstance).c_str()), typeRef); + } + } + } + + if (populateType > BfPopulateType_IdentityNoRemapAlias) + { + if (!ResolveTypeResult_Validate(typeRef, resolvedTypeRef)) + return NULL; + } if ((populateType != BfPopulateType_TypeDef) && (populateType != BfPopulateType_IdentityNoRemapAlias)) { @@ -10827,7 +10844,16 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula if (leftType == NULL) { BfAutoParentNodeEntry autoParentNodeEntry(this, qualifiedTypeRef); - leftType = ResolveTypeRef(qualifiedTypeRef->mLeft, BfPopulateType_Identity, (BfResolveTypeRefFlags)((resolveFlags | BfResolveTypeRefFlag_IgnoreLookupError) & ~BfResolveTypeRefFlag_Attribute)); // We throw an error below if we can't find the type + + auto leftPopulateType = BfPopulateType_Identity; + if ((resolveFlags & BfResolveTypeRefFlag_AllowUnboundGeneric) == 0) + { + // We can't just pass 'Identity' here because it won't validate a generic type ref on the left + leftPopulateType = BfPopulateType_Declaration; + } + + leftType = ResolveTypeRef(qualifiedTypeRef->mLeft, leftPopulateType, + (BfResolveTypeRefFlags)((resolveFlags | BfResolveTypeRefFlag_IgnoreLookupError) & ~BfResolveTypeRefFlag_Attribute)); // We throw an error below if we can't find the type } if (leftType == NULL)