From 584f47d025dfa693960951b202fd85a58d76dc6f Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sun, 25 Dec 2016 18:42:20 -0500 Subject: [PATCH 1/3] Sema: Fix crashes in CSDiag - TypeMatchVisitor doesn't like seeing ErrorTypes -- stop if we have one. - Common logic for replacing type parameters and variables with UnresolvedType. - Fix crash in coerceToType() if one of the two types has an UnresolvedType in it, but not at the top level. Fixes one compiler_crasher and some regressions with upcoming patches. --- lib/Sema/CSApply.cpp | 2 +- lib/Sema/CSDiag.cpp | 79 ++++++++++++------- ...stypevariable-output-hastypevariable.swift | 2 +- 3 files changed, 52 insertions(+), 31 deletions(-) rename validation-test/{compiler_crashers => compiler_crashers_fixed}/28561-input-hastypevariable-output-hastypevariable.swift (87%) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 1b848a99cef6..40402cc5893a 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -5852,7 +5852,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, auto toObjType = toType->getLValueOrInOutObjectType(); // Conversion to/from UnresolvedType (looking through @lvalue or inout). - if (fromObjType->is() || toObjType->is()) + if (fromObjType->hasUnresolvedType() || toObjType->hasUnresolvedType()) return cs.cacheType(new (tc.Context) UnresolvedTypeConversionExpr(expr, toType)); diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index d2f5bc68ea40..d6f4db8c23fe 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -1219,6 +1219,9 @@ static bool findGenericSubstitutions(DeclContext *dc, Type paramType, } }; + if (paramType->hasError()) + return false; + GenericVisitor visitor(dc, archetypesMap); return visitor.match(paramType, actualArgType); } @@ -1474,6 +1477,38 @@ CalleeCandidateInfo::evaluateCloseness(UncurriedCandidate candidate, return { closeness, {}}; } +/// Rewrite any type parameters in the specified type with UnresolvedType. +static Type replaceTypeParametersWithUnresolved(Type ty) { + if (!ty) return ty; + + if (!ty->hasTypeParameter() && !ty->hasArchetype()) return ty; + + auto &ctx = ty->getASTContext(); + + return ty.transform([&](Type type) -> Type { + if (type->is() || + type->isTypeParameter()) + return ctx.TheUnresolvedType; + return type; + }); +} + +/// Rewrite any type variables & archetypes in the specified type with +/// UnresolvedType. +static Type replaceTypeVariablesWithUnresolved(Type ty) { + if (!ty) return ty; + + if (!ty->hasTypeVariable()) return ty; + + auto &ctx = ty->getASTContext(); + + return ty.transform([&](Type type) -> Type { + if (type->isTypeVariableOrMember()) + return ctx.TheUnresolvedType; + return type; + }); +} + void CalleeCandidateInfo::collectCalleeCandidates(Expr *fn, bool implicitDotSyntax) { fn = fn->getValueProvidingExpr(); @@ -1552,21 +1587,23 @@ void CalleeCandidateInfo::collectCalleeCandidates(Expr *fn, collectCalleeCandidates(AE->getFn(), /*implicitDotSyntax=*/AE->getMemberOperatorRef()); - // If this is a DotSyntaxCallExpr, then the callee is a method, and the - // argument list of this apply is the base being applied to the method. - // If we have a type for that, capture it so that we can calculate a - // substituted type, which resolves many generic arguments. - Type baseType; - if (isa(AE) && - !isUnresolvedOrTypeVarType(AE->getArg()->getType())) - baseType = AE->getArg()->getType()->getLValueOrInOutObjectType(); - // If we found a candidate list with a recursive walk, try adjust the curry // level for the applied subexpression in this call. if (!candidates.empty()) { + // If this is a DotSyntaxCallExpr, then the callee is a method, and the + // argument list of this apply is the base being applied to the method. + // If we have a type for that, capture it so that we can calculate a + // substituted type, which resolves many generic arguments. + Type baseType; + if (isa(AE) && + !isUnresolvedOrTypeVarType(AE->getArg()->getType())) + baseType = AE->getArg()->getType()->getLValueOrInOutObjectType(); + for (auto &C : candidates) { C.level += 1; + baseType = replaceTypeVariablesWithUnresolved(baseType); + // Compute a new substituted type if we have a base type to apply. if (baseType && C.level == 1 && C.getDecl()) { C.entityType = baseType->getTypeOfMember(CS->DC->getParentModule(), @@ -1755,7 +1792,8 @@ CalleeCandidateInfo::CalleeCandidateInfo(Type baseType, // it to get a simpler and more concrete type. // if (baseType) { - auto substType = baseType; + auto substType = replaceTypeVariablesWithUnresolved(baseType); + // If this is a DeclViaUnwrappingOptional, then we're actually looking // through an optional to get the member, and baseType is an Optional or // Metatype. @@ -3366,22 +3404,6 @@ static void eraseOpenedExistentials(Expr *&expr) { expr = expr->walk(ExistentialEraser()); } -/// Rewrite any type variables & archetypes in the specified type with -/// UnresolvedType. -static Type replaceArchetypesAndTypeVarsWithUnresolved(Type ty) { - if (!ty) return ty; - - auto &ctx = ty->getASTContext(); - - return ty.transform([&](Type type) -> Type { - if (type->isTypeVariableOrMember() || - type->is() || - type->isTypeParameter()) - return ctx.TheUnresolvedType; - return type; - }); -} - /// Unless we've already done this, retypecheck the specified subexpression on /// its own, without including any contextual constraints or parent expr /// nodes. This is more likely to succeed than type checking the original @@ -3414,9 +3436,8 @@ Expr *FailureDiagnosis::typeCheckChildIndependently( convertType = FT->getResult(); // Replace archetypes and type parameters with UnresolvedType. - if (convertType->hasTypeVariable() || convertType->hasArchetype() || - convertType->hasTypeParameter()) - convertType = replaceArchetypesAndTypeVarsWithUnresolved(convertType); + convertType = replaceTypeParametersWithUnresolved(convertType); + convertType = replaceTypeVariablesWithUnresolved(convertType); // If the conversion type contains no info, drop it. if (convertType->is() || diff --git a/validation-test/compiler_crashers/28561-input-hastypevariable-output-hastypevariable.swift b/validation-test/compiler_crashers_fixed/28561-input-hastypevariable-output-hastypevariable.swift similarity index 87% rename from validation-test/compiler_crashers/28561-input-hastypevariable-output-hastypevariable.swift rename to validation-test/compiler_crashers_fixed/28561-input-hastypevariable-output-hastypevariable.swift index 083ebce8ed13..21c58fb3606a 100644 --- a/validation-test/compiler_crashers/28561-input-hastypevariable-output-hastypevariable.swift +++ b/validation-test/compiler_crashers_fixed/28561-input-hastypevariable-output-hastypevariable.swift @@ -5,6 +5,6 @@ // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// RUN: not --crash %target-swift-frontend %s -emit-ir +// RUN: not %target-swift-frontend %s -emit-ir // REQUIRES: asserts Set.init(""3 From 18adb53226c0e3a13353d22ec962bd75034e92ee Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 2 Jan 2017 21:27:42 -0800 Subject: [PATCH 2/3] Sema: Tighten up name lookup routines to not look through metatypes/lvalues/etc Previously all of the following would strip off varying amounts of MetatypeType, LValueType, InOutType, DynamicSelfType, etc: - ConstraintSystem::performMemberLookup() - ConstraintSystem::lookupMember() - TypeChecker::lookupMember() - DeclContext::lookupQualified() - Type::getContextSubstitutions() The problem is that the higher level methods that took a lookup type would call the lower level methods, and post-process the result using the given lookup type. Since different levels of sugar were stripped, it made the code hard to reason about and opened up edge cases, eg if a DynamicSelfType or InOutType appears where we didn't expect it. Since filtering out static/instance and mutating/nonmutating members is done at higher levels, there's no reason for these name lookup operations to accept anything other than nominal types, existentials and archetypes. Make this so with assertions, and deal with the fallout. --- include/swift/AST/Types.h | 14 +++++-- lib/AST/ASTPrinter.cpp | 13 ++++--- lib/AST/NameLookup.cpp | 13 +------ lib/AST/Type.cpp | 34 ++++++++--------- lib/IDE/CodeCompletion.cpp | 37 +++++++++++++------ lib/SILGen/SILGenMaterializeForSet.cpp | 3 +- lib/Sema/CSDiag.cpp | 21 +++++------ lib/Sema/CSSimplify.cpp | 25 +++++++------ lib/Sema/ConstraintSystem.cpp | 24 ++++-------- lib/Sema/MiscDiagnostics.cpp | 20 ++++++---- lib/Sema/TypeCheckAttr.cpp | 11 +++--- lib/Sema/TypeCheckConstraints.cpp | 12 +++--- lib/Sema/TypeCheckDecl.cpp | 3 +- lib/Sema/TypeCheckExprObjC.cpp | 4 +- lib/Sema/TypeCheckNameLookup.cpp | 32 +++------------- lib/Sema/TypeCheckType.cpp | 32 ++++++++-------- lib/Sema/TypeChecker.h | 4 +- test/expr/expressions.swift | 7 +++- .../28442-swift-typebase-getrvaluetype.swift | 2 +- 19 files changed, 152 insertions(+), 159 deletions(-) rename validation-test/{compiler_crashers => compiler_crashers_fixed}/28442-swift-typebase-getrvaluetype.swift (87%) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 8f4bf7c53171..2ccda2c3f31d 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -648,9 +648,17 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// with a class type. bool mayHaveSuperclass(); - /// \brief Determine whether this type may have nested types, which holds for - /// nominal types, existentials and archetypes. - bool mayHaveMemberTypes() { + /// \brief Determine whether this type can be used as a base type for AST + /// name lookup, which is the case for nominal types, protocol compositions + /// and archetypes. + /// + /// Generally, the static vs instanec and mutating vs nonmutating distinction + /// is handled elsewhere, so metatypes, lvalue types and inout types are not + /// allowed here. + /// + /// Similarly, tuples formally have members, but this does not go through + /// name lookup. + bool mayHaveMembers() { return (is() || is() || isExistentialType() || diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index fc6d99be64c7..459d481ee0fe 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -224,7 +224,7 @@ struct SynthesizedExtensionAnalyzer::Implementation { // the extension to the interface types of the base type's // declaration. TypeSubstitutionMap subMap; - if (!BaseType->isAnyExistentialType()) + if (!BaseType->isExistentialType()) subMap = BaseType->getContextSubstitutions(Ext); auto *M = DC->getParentModule(); @@ -453,7 +453,9 @@ void PrintOptions::clearSynthesizedExtension() { } TypeTransformContext::TypeTransformContext(Type T) - : BaseType(T.getPointer()) {} + : BaseType(T.getPointer()) { + assert(T->mayHaveMembers()); +} TypeTransformContext::TypeTransformContext(NominalTypeDecl *NTD) : BaseType(NTD->getDeclaredTypeInContext().getPointer()), Nominal(NTD) {} @@ -984,8 +986,9 @@ class PrintAST : public ASTVisitor { Type OldType = CurrentType; if (CurrentType && (Old != nullptr || Options.PrintAsMember)) { if (auto *NTD = dyn_cast(D)) { - CurrentType = CurrentType->getTypeOfMember( - Options.CurrentModule, NTD, nullptr); + CurrentType = NTD->getDeclaredInterfaceType().subst( + Options.CurrentModule, + CurrentType->getContextSubstitutions(NTD->getDeclContext())); } } @@ -1259,7 +1262,7 @@ void PrintAST::printSingleDepthOfGenericSignature( ModuleDecl *M = nullptr; if (CurrentType) { - if (!CurrentType->isAnyExistentialType()) { + if (!CurrentType->isExistentialType()) { auto *DC = Current->getInnermostDeclContext()->getInnermostTypeContext(); subMap = CurrentType->getContextSubstitutions(DC); M = DC->getParentModule(); diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 9b13448c4151..97dd541ffe48 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1316,17 +1316,6 @@ bool DeclContext::lookupQualified(Type type, } }; - // Look through lvalue and inout types. - type = type->getLValueOrInOutObjectType(); - - // Look through metatypes. - if (auto metaTy = type->getAs()) - type = metaTy->getInstanceType(); - - // Look through DynamicSelf. - if (auto dynamicSelf = type->getAs()) - type = dynamicSelf->getSelfType(); - // Look for module references. if (auto moduleTy = type->getAs()) { Module *module = moduleTy->getModule(); @@ -1438,6 +1427,8 @@ bool DeclContext::lookupQualified(Type type, } } } + } else { + llvm_unreachable("Bad type for qualified lookup"); } // Allow filtering of the visible declarations based on various diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 21efa3093d3b..70308810637b 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -3099,23 +3099,15 @@ Type TypeBase::getSuperclassForDecl(const ClassDecl *baseClass, } TypeSubstitutionMap TypeBase::getContextSubstitutions(const DeclContext *dc) { + assert(dc->isTypeContext()); + Type baseTy(this); - // Ignore lvalues in the base type. - Type baseTy(getRValueType()); - - // Look through the metatype; it has no bearing on the result. - if (auto metaBase = baseTy->getAs()) { - baseTy = metaBase->getInstanceType()->getRValueType(); - } + assert(!baseTy->isLValueType() && !baseTy->is()); // The resulting set of substitutions. Always use this to ensure we // don't miss out on NRVO anywhere. TypeSubstitutionMap substitutions; - // Look through non-type contexts. - while (!dc->isTypeContext()) - dc = dc->getParent(); - // If the member is part of a protocol or extension thereof, we need // to substitute in the type of Self. if (dc->getAsProtocolOrProtocolExtensionContext()) { @@ -3127,19 +3119,24 @@ TypeSubstitutionMap TypeBase::getContextSubstitutions(const DeclContext *dc) { return substitutions; } + // If we found a member of a concrete type from a protocol extension, + // get the superclass out of the archetype. + if (auto *archetypeTy = baseTy->getAs()) + baseTy = archetypeTy->getSuperclass(); + // Extract the lazy resolver. LazyResolver *resolver = dc->getASTContext().getLazyResolver(); // Find the superclass type with the context matching that of the member. // // FIXME: Do this in the caller? - if (baseTy->getAnyNominal()) { - auto *ownerNominal = dc->getAsNominalTypeOrNominalTypeExtensionContext(); - if (auto *ownerClass = dyn_cast(ownerNominal)) - baseTy = baseTy->getSuperclassForDecl(ownerClass, resolver); + assert(baseTy->getAnyNominal()); - assert(ownerNominal == baseTy->getAnyNominal()); - } + auto *ownerNominal = dc->getAsNominalTypeOrNominalTypeExtensionContext(); + if (auto *ownerClass = dyn_cast(ownerNominal)) + baseTy = baseTy->getSuperclassForDecl(ownerClass, resolver); + + assert(ownerNominal == baseTy->getAnyNominal()); // If the base type isn't specialized, there's nothing to substitute. if (!baseTy->isSpecialized()) @@ -3177,8 +3174,7 @@ TypeSubstitutionMap TypeBase::getContextSubstitutions(const DeclContext *dc) { continue; } - // We're done. - break; + llvm_unreachable("Bad base type"); } return substitutions; diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index ce47bf801257..78f391d39417 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -1975,8 +1975,13 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { Type ContextTy = VD->getDeclContext()->getDeclaredInterfaceType(); if (ContextTy) { // Look through lvalue types and metatypes - Type MaybeNominalType = (*ExprType)->getRValueType() - ->getRValueInstanceType(); + Type MaybeNominalType = (*ExprType)->getRValueType(); + + if (auto Metatype = MaybeNominalType->getAs()) + MaybeNominalType = Metatype->getInstanceType(); + + if (auto SelfType = MaybeNominalType->getAs()) + MaybeNominalType = SelfType->getSelfType(); // For optional protocol requirements and dynamic dispatch, // strip off optionality from the base type, but only if @@ -1989,6 +1994,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (MaybeNominalType->isAnyObject()) return T; + // FIXME: Sometimes ExprType is the type of the member here, + // and not the type of the base. That is inconsistent and + // should be cleaned up. + if (!MaybeNominalType->mayHaveMembers()) + return T; + // For everything else, substitute in the base type. auto Subs = MaybeNominalType->getMemberSubstitutions(VD); @@ -2865,23 +2876,24 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (auto *NTD = dyn_cast(D)) { addNominalTypeRef(NTD, Reason); - addConstructorCallsForType(NTD->getInterfaceType(), NTD->getName(), - Reason); + addConstructorCallsForType(NTD->getDeclaredInterfaceType(), + NTD->getName(), Reason); return; } if (auto *TAD = dyn_cast(D)) { addTypeAliasRef(TAD, Reason); auto type = TAD->mapTypeIntoContext(TAD->getUnderlyingTypeLoc().getType()); - addConstructorCallsForType(type, TAD->getName(), Reason); + if (type->mayHaveMembers()) + addConstructorCallsForType(type, TAD->getName(), Reason); return; } if (auto *GP = dyn_cast(D)) { addGenericTypeParamRef(GP, Reason); for (auto *protocol : GP->getConformingProtocols()) - addConstructorCallsForType(protocol->getInterfaceType(), GP->getName(), - Reason); + addConstructorCallsForType(protocol->getDeclaredInterfaceType(), + GP->getName(), Reason); return; } @@ -2934,23 +2946,24 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (auto *NTD = dyn_cast(D)) { addNominalTypeRef(NTD, Reason); - addConstructorCallsForType(NTD->getInterfaceType(), NTD->getName(), - Reason); + addConstructorCallsForType(NTD->getDeclaredInterfaceType(), + NTD->getName(), Reason); return; } if (auto *TAD = dyn_cast(D)) { addTypeAliasRef(TAD, Reason); auto type = TAD->mapTypeIntoContext(TAD->getDeclaredInterfaceType()); - addConstructorCallsForType(type, TAD->getName(), Reason); + if (type->mayHaveMembers()) + addConstructorCallsForType(type, TAD->getName(), Reason); return; } if (auto *GP = dyn_cast(D)) { addGenericTypeParamRef(GP, Reason); for (auto *protocol : GP->getConformingProtocols()) - addConstructorCallsForType(protocol->getInterfaceType(), GP->getName(), - Reason); + addConstructorCallsForType(protocol->getDeclaredInterfaceType(), + GP->getName(), Reason); return; } diff --git a/lib/SILGen/SILGenMaterializeForSet.cpp b/lib/SILGen/SILGenMaterializeForSet.cpp index 53345c2d2277..6734d430b691 100644 --- a/lib/SILGen/SILGenMaterializeForSet.cpp +++ b/lib/SILGen/SILGenMaterializeForSet.cpp @@ -385,7 +385,8 @@ struct MaterializeForSetEmitter { /// Given part of the witness's interface type, produce its /// substitution according to the witness substitutions. CanType getSubstWitnessInterfaceType(CanType type) { - auto subs = SubstSelfType->getMemberSubstitutions(WitnessStorage); + auto subs = SubstSelfType->getRValueInstanceType() + ->getMemberSubstitutions(WitnessStorage); return type.subst(SGM.SwiftModule, subs)->getCanonicalType(); } diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index d6f4db8c23fe..e37867af1d9e 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -1606,6 +1606,9 @@ void CalleeCandidateInfo::collectCalleeCandidates(Expr *fn, // Compute a new substituted type if we have a base type to apply. if (baseType && C.level == 1 && C.getDecl()) { + baseType = baseType + ->getLValueOrInOutObjectType() + ->getRValueInstanceType(); C.entityType = baseType->getTypeOfMember(CS->DC->getParentModule(), C.getDecl(), nullptr); C.substituted = true; @@ -1793,22 +1796,18 @@ CalleeCandidateInfo::CalleeCandidateInfo(Type baseType, // if (baseType) { auto substType = replaceTypeVariablesWithUnresolved(baseType); + if (substType) + substType = substType + ->getLValueOrInOutObjectType() + ->getRValueInstanceType(); // If this is a DeclViaUnwrappingOptional, then we're actually looking // through an optional to get the member, and baseType is an Optional or // Metatype. if (cand.getKind() == OverloadChoiceKind::DeclViaUnwrappedOptional) { - bool isMeta = false; - if (auto MTT = substType->getAs()) { - isMeta = true; - substType = MTT->getInstanceType(); - } - // Look through optional or IUO to get the underlying type the decl was // found in. substType = substType->getAnyOptionalObjectType(); - if (isMeta && substType) - substType = MetatypeType::get(substType); } else if (cand.getKind() != OverloadChoiceKind::Decl) { // Otherwise, if it is a remapping we can't handle, don't try to compute // a substitution. @@ -2330,8 +2329,8 @@ bool FailureDiagnosis::diagnoseGeneralMemberFailure(Constraint *constraint) { anchor = typeCheckArbitrarySubExprIndependently(anchor, TCC_AllowLValue); if (!anchor) return true; - auto baseTy = anchor->getType(); - auto baseObjTy = baseTy->getRValueType(); + auto baseTy = anchor->getType()->getLValueOrInOutObjectType(); + auto baseObjTy = baseTy; // If the base type is an IUO, look through it. Odds are, the code is not // trying to find a member of it. @@ -3105,7 +3104,7 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){ // contain a member var 'boolValue', now does not convert to Bool. This block // tries to add a specific diagnosis/fixit to explicitly invoke 'boolValue'. if (toType->isBool() && - fromType->mayHaveMemberTypes()) { + fromType->mayHaveMembers()) { auto LookupResult = CS->TC.lookupMember(CS->DC, fromType, DeclName(CS->TC.Context.getIdentifier("boolValue"))); if (!LookupResult.empty()) { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index af3fc9b3c055..6157032f1d65 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2644,9 +2644,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, return result; } - if (instanceTy->isTypeParameter()) - return MemberLookupResult(); - // Okay, start building up the result list. MemberLookupResult result; result.OverallResult = MemberLookupResult::HasResults; @@ -2676,7 +2673,12 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, result.ViableCandidates.push_back(OverloadChoice(baseTy, fieldIdx)); return result; } - + + if (auto *selfTy = instanceTy->getAs()) + instanceTy = selfTy->getSelfType(); + + if (!instanceTy->mayHaveMembers()) + return result; // If we have a simple name, determine whether there are argument // labels we can use to restrict the set of lookup results. @@ -2741,7 +2743,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, // extensions cannot yet define designated initializers. lookupOptions -= NameLookupFlags::PerformConformanceCheck; - LookupResult ctors = TC.lookupConstructors(DC, baseObjTy, lookupOptions); + LookupResult ctors = TC.lookupConstructors(DC, instanceTy, lookupOptions); if (!ctors) return result; // No result. @@ -2831,7 +2833,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, } // Look for members within the base. - LookupResult &lookup = lookupMember(baseObjTy, memberName); + LookupResult &lookup = lookupMember(instanceTy, memberName); // The set of directly accessible types, which is only used when // we're performing dynamic lookup into an existential type. @@ -3017,10 +3019,11 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, if (result.ViableCandidates.empty() && isMetatype && constraintKind == ConstraintKind::UnresolvedValueMember) { if (auto objectType = instanceTy->getAnyOptionalObjectType()) { - LookupResult &optionalLookup = lookupMember(MetatypeType::get(objectType), - memberName); - for (auto result : optionalLookup) - addChoice(result, /*bridged*/false, /*isUnwrappedOptional=*/true); + if (objectType->mayHaveMembers()) { + LookupResult &optionalLookup = lookupMember(objectType, memberName); + for (auto result : optionalLookup) + addChoice(result, /*bridged*/false, /*isUnwrappedOptional=*/true); + } } } @@ -3045,7 +3048,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, // This is only used for diagnostics, so always use KnownPrivate. lookupOptions |= NameLookupFlags::KnownPrivate; - auto lookup = TC.lookupMember(DC, baseObjTy->getCanonicalType(), + auto lookup = TC.lookupMember(DC, instanceTy, memberName, lookupOptions); for (auto cand : lookup) { // If the result is invalid, skip it. diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index cfed43830713..9e67c7d44b88 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -214,10 +214,7 @@ LookupResult &ConstraintSystem::lookupMember(Type base, DeclName name) { result = std::move(lookup); // If we aren't performing dynamic lookup, we're done. - auto instanceTy = base->getRValueType(); - if (auto metaTy = instanceTy->getAs()) - instanceTy = metaTy->getInstanceType(); - auto protoTy = instanceTy->getAs(); + auto protoTy = base->getAs(); if (!*result || !protoTy || !protoTy->getDecl()->isSpecificProtocol( @@ -1568,15 +1565,7 @@ Type ConstraintSystem::simplifyType(Type type) { if (newBase.getPointer() == depMemTy->getBase().getPointer()) return type; - Type lookupBaseType = newBase; - - // Look through an inout type. - if (auto inout = lookupBaseType->getAs()) - lookupBaseType = inout->getObjectType(); - - // Look through a metatype. - if (auto metatype = lookupBaseType->getAs()) - lookupBaseType = metatype->getInstanceType(); + Type lookupBaseType = newBase->getLValueOrInOutObjectType(); // If the new base is still something we can't handle, just build a // new dependent member type. @@ -1592,9 +1581,12 @@ Type ConstraintSystem::simplifyType(Type type) { auto assocType = depMemTy->getAssocType(); assert(depMemTy->getAssocType()); - auto result = lookupBaseType->getTypeOfMember( - DC->getParentModule(), assocType, &TC, - assocType->getDeclaredInterfaceType()); + if (!lookupBaseType->mayHaveMembers()) return type; + + auto result = assocType->getDeclaredInterfaceType() + .subst(DC->getParentModule(), + lookupBaseType->getContextSubstitutions( + assocType->getDeclContext())); // FIXME: Record failure somehow? if (!result) return type; diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 68a37e713871..1ef1b1562120 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -521,13 +521,16 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // non-existential type and has any initializers. auto eTy = E->getType(); bool isExistential = false; - if (auto metaTy = eTy->getAs()) - isExistential = metaTy->getInstanceType()->isAnyExistentialType(); - if (!isExistential && - !eTy->is() && - !TC.lookupConstructors(const_cast(DC), eTy).empty()) { - TC.diagnose(E->getEndLoc(), diag::add_parens_to_type) - .fixItInsertAfter(E->getEndLoc(), "()"); + if (auto metaTy = E->getType()->getAs()) { + auto instanceTy = metaTy->getInstanceType(); + isExistential = instanceTy->isExistentialType(); + if (!isExistential && + instanceTy->mayHaveMembers() && + !TC.lookupConstructors(const_cast(DC), + instanceTy).empty()) { + TC.diagnose(E->getEndLoc(), diag::add_parens_to_type) + .fixItInsertAfter(E->getEndLoc(), "()"); + } } // Add fix-it to insert ".self". @@ -3319,7 +3322,8 @@ class ObjCSelectorWalker : public ASTWalker { auto nominal = method->getDeclContext()->getAsNominalTypeOrNominalTypeExtensionContext(); auto result = TC.lookupMember(const_cast(DC), - nominal->getInterfaceType(), lookupName, + nominal->getDeclaredInterfaceType(), + lookupName, (defaultMemberLookupOptions | NameLookupFlags::KnownPrivate)); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index e4136a55d13f..aaa371b7e5ce 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -1152,14 +1152,13 @@ void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, ProtocolDecl *ApplicationDelegateProto = nullptr; if (KitModule) { auto lookupOptions = defaultUnqualifiedLookupOptions; - lookupOptions |= NameLookupFlags::OnlyTypes; lookupOptions |= NameLookupFlags::KnownPrivate; - auto lookup = TC.lookupUnqualified(KitModule, Id_ApplicationDelegate, - SourceLoc(), - lookupOptions); - ApplicationDelegateProto = dyn_cast_or_null( - lookup.getSingleTypeResult()); + auto lookup = TC.lookupUnqualifiedType(KitModule, Id_ApplicationDelegate, + SourceLoc(), + lookupOptions); + if (lookup.size() == 1) + ApplicationDelegateProto = dyn_cast(lookup[0]); } if (!ApplicationDelegateProto || diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index bf7741050f43..e1fd79aa63fc 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2048,17 +2048,17 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { // types of existentials. // // We will diagnose it later. - if (!sequenceType->isExistentialType()) { + if (!sequenceType->isExistentialType() && + (sequenceType->mayHaveMembers() || + sequenceType->isTypeVariableOrMember())) { ASTContext &ctx = tc.Context; auto iteratorAssocType = cast( sequenceProto->lookupDirect(ctx.Id_Iterator).front()); - iteratorType = sequenceType->getTypeOfMember( - cs.DC->getParentModule(), - iteratorAssocType, - &tc, - iteratorAssocType->getDeclaredInterfaceType()); + iteratorType = iteratorAssocType->getDeclaredInterfaceType() + .subst(cs.DC->getParentModule(), + sequenceType->getContextSubstitutions(sequenceProto)); if (iteratorType) { auto iteratorProto = diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index d69aaf614ea5..1d82bf30828b 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -5343,7 +5343,6 @@ class DeclChecker : public DeclVisitor { // one. auto attempt = OverrideCheckingAttempt::PerfectMatch; SmallVector matches; - auto superclassMetaTy = MetatypeType::get(superclass); DeclName name = decl->getFullName(); bool hadExactMatch = false; LookupResult members; @@ -5387,7 +5386,7 @@ class DeclChecker : public DeclVisitor { lookupOptions -= NameLookupFlags::ProtocolMembers; lookupOptions -= NameLookupFlags::PerformConformanceCheck; - members = TC.lookupMember(decl->getDeclContext(), superclassMetaTy, + members = TC.lookupMember(decl->getDeclContext(), superclass, name, lookupOptions); } diff --git a/lib/Sema/TypeCheckExprObjC.cpp b/lib/Sema/TypeCheckExprObjC.cpp index f2e0351d8e21..cedc21b176fb 100644 --- a/lib/Sema/TypeCheckExprObjC.cpp +++ b/lib/Sema/TypeCheckExprObjC.cpp @@ -330,8 +330,8 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, Type newType; if (lookupType && !lookupType->isAnyObject()) { - newType = lookupType->getTypeOfMember(dc->getParentModule(), type, - this); + newType = lookupType->getTypeOfMember(dc->getParentModule(), type, this, + type->getDeclaredInterfaceType()); } else { newType = type->getDeclaredInterfaceType(); } diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index aab0e4cf8a40..8a8e9104409b 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -99,10 +99,6 @@ namespace { /// \param foundInType The type through which we found the /// declaration. void add(ValueDecl *found, ValueDecl *base, Type foundInType) { - // If we only want types, AST name lookup should not yield anything else. - assert(!Options.contains(NameLookupFlags::OnlyTypes) || - isa(found)); - ConformanceCheckOptions conformanceOptions; if (Options.contains(NameLookupFlags::KnownPrivate)) conformanceOptions |= ConformanceCheckFlags::InExpression; @@ -181,7 +177,7 @@ LookupResult TypeChecker::lookupUnqualified(DeclContext *dc, DeclName name, name, dc, this, options.contains(NameLookupFlags::KnownPrivate), loc, - options.contains(NameLookupFlags::OnlyTypes), + /*OnlyTypes=*/false, options.contains(NameLookupFlags::ProtocolMembers), options.contains(NameLookupFlags::IgnoreAccessibility)); @@ -236,6 +232,8 @@ TypeChecker::lookupUnqualifiedType(DeclContext *dc, DeclName name, LookupResult TypeChecker::lookupMember(DeclContext *dc, Type type, DeclName name, NameLookupOptions options) { + assert(type->mayHaveMembers()); + LookupResult result; NLOptions subOptions = NL_QualifiedDefault; if (options.contains(NameLookupFlags::KnownPrivate)) @@ -244,19 +242,8 @@ LookupResult TypeChecker::lookupMember(DeclContext *dc, subOptions |= NL_DynamicLookup; if (options.contains(NameLookupFlags::IgnoreAccessibility)) subOptions |= NL_IgnoreAccessibility; - if (options.contains(NameLookupFlags::OnlyTypes)) - subOptions |= NL_OnlyTypes; - - // Dig out the type that we'll actually be looking into, and determine - // whether it is a nominal type. - Type lookupType = type; - if (auto lvalueType = lookupType->getAs()) { - lookupType = lvalueType->getObjectType(); - } - if (auto metaType = lookupType->getAs()) { - lookupType = metaType->getInstanceType(); - } - NominalTypeDecl *nominalLookupType = lookupType->getAnyNominal(); + + NominalTypeDecl *nominalLookupType = type->getAnyNominal(); if (options.contains(NameLookupFlags::ProtocolMembers)) subOptions |= NL_ProtocolMembers; @@ -265,9 +252,6 @@ LookupResult TypeChecker::lookupMember(DeclContext *dc, subOptions &= ~NL_RemoveOverridden; subOptions &= ~NL_RemoveNonVisible; - // We can't have tuple types here; they need to be handled elsewhere. - assert(!type->is()); - // Local function that performs lookup. auto doLookup = [&]() { result.clear(); @@ -278,7 +262,7 @@ LookupResult TypeChecker::lookupMember(DeclContext *dc, dc->lookupQualified(type, name, subOptions, this, lookupResults); for (auto found : lookupResults) { - builder.add(found, nominalLookupType, lookupType); + builder.add(found, nominalLookupType, type); } }; @@ -310,10 +294,6 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc, NameLookupOptions options) { LookupTypeResult result; - // Structural types do not have member types. - if (!type->mayHaveMemberTypes()) - return result; - // Look for members with the given name. SmallVector decls; NLOptions subOptions = NL_QualifiedDefault | NL_OnlyTypes; diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 0ca99c156620..99ae855919ed 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -134,12 +134,11 @@ static Type getObjectiveCNominalType(TypeChecker &TC, NameLookupOptions lookupOptions = defaultMemberLookupOptions | - NameLookupFlags::KnownPrivate | - NameLookupFlags::OnlyTypes; - if (auto result = TC.lookupMember(dc, ModuleType::get(module), TypeName, - lookupOptions)) { - for (auto decl : result) { - if (auto nominal = dyn_cast(decl.Decl)) { + NameLookupFlags::KnownPrivate; + if (auto result = TC.lookupMemberType(dc, ModuleType::get(module), TypeName, + lookupOptions)) { + for (auto pair : result) { + if (auto nominal = dyn_cast(pair.first)) { cache = nominal->getDeclaredType(); return cache; } @@ -775,7 +774,6 @@ static Type diagnoseUnknownType(TypeChecker &tc, DeclContext *dc, NameLookupOptions relookupOptions = lookupOptions; relookupOptions |= NameLookupFlags::KnownPrivate; relookupOptions |= NameLookupFlags::IgnoreAccessibility; - relookupOptions |= NameLookupFlags::OnlyTypes; auto inaccessibleResults = tc.lookupUnqualifiedType(lookupDC, comp->getIdentifier(), comp->getIdLoc(), relookupOptions); @@ -831,6 +829,12 @@ static Type diagnoseUnknownType(TypeChecker &tc, DeclContext *dc, } // Qualified lookup case. + if (!parentType->mayHaveMembers()) { + tc.diagnose(comp->getIdLoc(), diag::invalid_member_type, + comp->getIdentifier(), parentType) + .highlight(parentRange); + return ErrorType::get(tc.Context); + } // Try ignoring access control. NameLookupOptions relookupOptions = lookupOptions; @@ -883,12 +887,9 @@ static Type diagnoseUnknownType(TypeChecker &tc, DeclContext *dc, NameLookupOptions memberLookupOptions = lookupOptions; memberLookupOptions |= NameLookupFlags::IgnoreAccessibility; memberLookupOptions |= NameLookupFlags::KnownPrivate; - memberLookupOptions -= NameLookupFlags::OnlyTypes; - if (parentType->mayHaveMemberTypes()) { - memberLookup = tc.lookupMember(dc, parentType, comp->getIdentifier(), - memberLookupOptions); - } + memberLookup = tc.lookupMember(dc, parentType, comp->getIdentifier(), + memberLookupOptions); // Looks like this is not a member type, but simply a member of parent type. if (!memberLookup.empty()) { @@ -1028,7 +1029,6 @@ resolveTopLevelIdentTypeComponent(TypeChecker &TC, DeclContext *DC, return nullptr; NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; - lookupOptions |= NameLookupFlags::OnlyTypes; if (options.contains(TR_KnownNonCascadingDependency)) lookupOptions |= NameLookupFlags::KnownPrivate; // FIXME: Eliminate this once we can handle finding protocol members @@ -1229,8 +1229,10 @@ static Type resolveNestedIdentTypeComponent( if (options.contains(TR_ExtensionBinding) || options.contains(TR_InheritanceClause)) lookupOptions -= NameLookupFlags::ProtocolMembers; - auto memberTypes = TC.lookupMemberType(DC, parentTy, comp->getIdentifier(), - lookupOptions); + LookupTypeResult memberTypes; + if (parentTy->mayHaveMembers()) + memberTypes = TC.lookupMemberType(DC, parentTy, comp->getIdentifier(), + lookupOptions); // Name lookup was ambiguous. Complain. // FIXME: Could try to apply generic arguments first, and see whether diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 456375e0b0a5..368fa5a07e3c 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -246,11 +246,9 @@ enum class NameLookupFlags { /// Whether to perform 'dynamic' name lookup that finds @objc /// members of any class or protocol. DynamicLookup = 0x08, - /// Whether we're only looking for types. - OnlyTypes = 0x10, /// Whether to ignore access control for this lookup, allowing inaccessible /// results to be returned. - IgnoreAccessibility = 0x20, + IgnoreAccessibility = 0x10, }; /// A set of options that control name lookup. diff --git a/test/expr/expressions.swift b/test/expr/expressions.swift index 27595b944691..0828286e680d 100644 --- a/test/expr/expressions.swift +++ b/test/expr/expressions.swift @@ -645,13 +645,18 @@ func lvalue_processing() { struct Foo { func method() {} + mutating func mutatingMethod() {} } func test() { var x = Foo() + let y = Foo() + + // FIXME: Bad diagnostics // rdar://15708430 - (&x).method() // expected-error {{'inout Foo' is not convertible to 'Foo'}} + (&x).method() // expected-error {{type of expression is ambiguous without more context}} + (&x).mutatingMethod() // expected-error {{cannot use mutating member on immutable value of type 'inout Foo'}} } diff --git a/validation-test/compiler_crashers/28442-swift-typebase-getrvaluetype.swift b/validation-test/compiler_crashers_fixed/28442-swift-typebase-getrvaluetype.swift similarity index 87% rename from validation-test/compiler_crashers/28442-swift-typebase-getrvaluetype.swift rename to validation-test/compiler_crashers_fixed/28442-swift-typebase-getrvaluetype.swift index 38dd4933d348..1e3169218bc6 100644 --- a/validation-test/compiler_crashers/28442-swift-typebase-getrvaluetype.swift +++ b/validation-test/compiler_crashers_fixed/28442-swift-typebase-getrvaluetype.swift @@ -5,6 +5,6 @@ // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// RUN: not --crash %target-swift-frontend %s -typecheck +// RUN: not %target-swift-frontend %s -typecheck // REQUIRES: asserts var f={var f=[]f.init From 41dde887781cda6dd1c67123c833c687b1e1ed69 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 2 Jan 2017 21:35:55 -0800 Subject: [PATCH 3/3] Sema: Allow unqualified type lookup to find typealiases in protocols Not really specific to typealiases; but we don't allow nominal types to be nested inside protocols yet, and all other types of protocol members have witnesses in the concrete type. Fixes . --- lib/Sema/TypeCheckNameLookup.cpp | 28 ++++++++++++++++++++++++---- lib/Sema/TypeCheckType.cpp | 3 --- test/Generics/associated_types.swift | 3 +-- test/decl/ext/generic.swift | 6 ++++++ test/decl/typealias/protocol.swift | 9 ++++----- 5 files changed, 35 insertions(+), 14 deletions(-) diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 8a8e9104409b..547d418366ef 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -214,18 +214,38 @@ SmallVector TypeChecker::lookupUnqualifiedType(DeclContext *dc, DeclName name, SourceLoc loc, NameLookupOptions options) { + SmallVector decls; + + // Try lookup without ProtocolMembers first. UnqualifiedLookup lookup( name, dc, this, options.contains(NameLookupFlags::KnownPrivate), loc, /*OnlyTypes=*/true, - options.contains(NameLookupFlags::ProtocolMembers), + /*ProtocolMembers=*/false, options.contains(NameLookupFlags::IgnoreAccessibility)); - - LookupResult result; - SmallVector decls; for (auto found : lookup.Results) decls.push_back(cast(found.getValueDecl())); + + if (decls.empty() && + options.contains(NameLookupFlags::ProtocolMembers)) { + // Try again, this time with protocol members. + // + // FIXME: Fix the problem where if NominalTypeDecl::getAllProtocols() + // is called too early, we start resolving extensions -- even those + // which do provide conformances. + UnqualifiedLookup lookup( + name, dc, this, + options.contains(NameLookupFlags::KnownPrivate), + loc, + /*OnlyTypes=*/true, + /*ProtocolMembers=*/true, + options.contains(NameLookupFlags::IgnoreAccessibility)); + + for (auto found : lookup.Results) + decls.push_back(cast(found.getValueDecl())); + } + return decls; } diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 99ae855919ed..a542949bbf72 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1031,9 +1031,6 @@ resolveTopLevelIdentTypeComponent(TypeChecker &TC, DeclContext *DC, NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; if (options.contains(TR_KnownNonCascadingDependency)) lookupOptions |= NameLookupFlags::KnownPrivate; - // FIXME: Eliminate this once we can handle finding protocol members - // in resolveTypeInContext. - lookupOptions -= NameLookupFlags::ProtocolMembers; auto globals = TC.lookupUnqualifiedType(lookupDC, comp->getIdentifier(), comp->getIdLoc(), diff --git a/test/Generics/associated_types.swift b/test/Generics/associated_types.swift index 282856104b8a..50fae7c3a0dc 100644 --- a/test/Generics/associated_types.swift +++ b/test/Generics/associated_types.swift @@ -21,8 +21,7 @@ struct Z : Fooable { var a : AssocType // expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} {{9-10=_}} } - // FIXME: We should be able to find this. - func blarg() -> AssocType {} // expected-error{{use of undeclared type 'AssocType'}} + func blarg() -> AssocType {} func wonka() -> Z.AssocType {} } diff --git a/test/decl/ext/generic.swift b/test/decl/ext/generic.swift index a70281dbcca1..a478406bf218 100644 --- a/test/decl/ext/generic.swift +++ b/test/decl/ext/generic.swift @@ -152,3 +152,9 @@ struct S4: P4 { } extension S4 where T : P5 {} + +struct S5 { + init(_: Q) { } +} + +extension S5 : P4 {} diff --git a/test/decl/typealias/protocol.swift b/test/decl/typealias/protocol.swift index 85a115f5f889..253ec3bfe47e 100644 --- a/test/decl/typealias/protocol.swift +++ b/test/decl/typealias/protocol.swift @@ -154,9 +154,9 @@ struct T5 : P5 { var v2: P5.T2 // expected-error {{typealias 'T2' can only be used with a concrete type or generic parameter base}} var v3: P5.X // expected-error {{typealias 'X' can only be used with a concrete type or generic parameter base}} - // FIXME: Unqualified reference to typealias from a protocol conformance - var v4: T1 // expected-error {{use of undeclared type 'T1'}} - var v5: T2 // expected-error {{use of undeclared type 'T2'}} + // Unqualified reference to typealias from a protocol conformance + var v4: T1 // OK + var v5: T2 // OK // Qualified reference var v6: T5.T1 // OK @@ -178,8 +178,7 @@ extension P7 { struct S7 : P7 { typealias A = Int - // FIXME - func inTypeContext(y: Y) // expected-error {{use of undeclared type 'Y'}} + func inTypeContext(y: Y) { } func inExpressionContext() { _ = Y.self