diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index c33430977328d..ad80618192181 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -51,6 +51,7 @@ TYPE_ATTR(autoclosure) TYPE_ATTR(convention) TYPE_ATTR(noescape) TYPE_ATTR(escaping) +TYPE_ATTR(qualified) // SIL-specific attributes TYPE_ATTR(block_storage) diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 957a8e78d8e95..52456f281cbb4 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -136,6 +136,8 @@ class UnqualifiedLookup { /// This lookup should include results from outside the innermost scope with /// results. IncludeOuterResults = 0x10, + /// Look up only modules visible at this location, not other kinds of names. + IncludeOnlyModules = 0x20, }; using Options = OptionSet; diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index a94fdf2e086bd..8256d54ee41cd 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1163,11 +1163,18 @@ class Parser { using TypeResult = ParsedSyntaxResult; using TypeErrorResult = ParsedSyntaxResult; + enum class ParseTypeFlags : uint8_t { + IgnoreCodeCompletion = 1 << 0, + IsSILFuncDecl = 1 << 1, + Minimal = 1 << 2 + }; + using ParseTypeOptions = OptionSet; + LayoutConstraint parseLayoutConstraint(Identifier LayoutConstraintID); - TypeASTResult parseType(); - TypeASTResult parseType(Diag<> MessageID, bool HandleCodeCompletion = true, - bool IsSILFuncDecl = false); + TypeASTResult parseType(ParseTypeOptions Flags = {}); + TypeASTResult parseType(Diag<> MessageID, + ParseTypeOptions Flags = {}); ParserStatus parseGenericArgumentsAST(llvm::SmallVectorImpl &ArgsAST, SourceLoc &LAngleLoc, SourceLoc &RAngleLoc); @@ -1175,15 +1182,17 @@ class Parser { const TypeAttributes &attrs, Optional &GenericsScope); TypeASTResult parseTypeSimpleOrCompositionAST(Diag<> MessageID, - bool HandleCodeCompletion); + ParseTypeOptions Flags = {}); TypeASTResult parseAnyTypeAST(); ParsedSyntaxResult parseGenericArgumentClauseSyntax(); - TypeResult parseTypeSimple(Diag<> MessageID, bool HandleCodeCompletion); - TypeResult parseTypeSimpleOrComposition(Diag<> MessageID, bool HandleCodeCompletion); - TypeResult parseTypeIdentifier(); + TypeResult parseTypeSimple(Diag<> MessageID, + ParseTypeOptions Flags = {}); + TypeResult parseTypeSimpleOrComposition(Diag<> MessageID, + ParseTypeOptions Flags = {}); + TypeResult parseTypeIdentifier(ParseTypeOptions Flags = {}); TypeResult parseAnyType(); TypeResult parseTypeTupleBody(); TypeResult parseTypeCollection(); diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index 90a203ab7180a..e14fb25ad6c41 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -311,6 +311,9 @@ void AttributedTypeRepr::printAttrs(ASTPrinter &Printer, Printer.printStructurePost(PrintStructureKind::BuiltinAttribute); Printer << " "; } + + if (hasAttr(TAK_qualified)) + Printer.printSimpleAttr("@qualified") << " "; } IdentTypeRepr *IdentTypeRepr::create(ASTContext &C, diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 6e4ff2b8cfe67..82333326bce5f 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -476,6 +476,12 @@ void UnqualifiedLookupFactory::performUnqualifiedLookup() { #endif SharedTimer timer("UnqualifiedLookupFactory performUnqualifiedLookup"); + if (options.contains(UnqualifiedLookup::Flags::IncludeOnlyModules)) { + lookForAModuleWithTheGivenName(DC); + recordCompletionOfAScope(); + return; + } + const Optional initialIsCascadingUse = getInitialIsCascadingUse(); ContextAndUnresolvedIsCascadingUse contextAndIsCascadingUse{ diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 6ef457a27abcd..3640e223e21f1 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -402,8 +402,8 @@ ParserResult Parser::parseExprSequenceElement(Diag<> message, consumeToken(); } - // Try to parse '@' sign or 'inout' as a attributed typerepr. - if (Tok.isAny(tok::at_sign, tok::kw_inout)) { + // Try to parse 'inout' as a attributed typerepr. + if (Tok.isAny(tok::kw_inout)) { bool isType = false; { BacktrackingScope backtrack(*this); @@ -411,9 +411,10 @@ ParserResult Parser::parseExprSequenceElement(Diag<> message, } if (isType) { ParserResult ty = parseType(); - if (ty.isNonNull()) - return makeParserResult( - new (Context) TypeExpr(TypeLoc(ty.get(), Type()))); + if (ty.isNonNull()) { + auto E = new (Context) TypeExpr(TypeLoc(ty.get(), Type())); + return makeParserResult(static_cast(E)); + } checkForInputIncomplete(); return nullptr; } @@ -1480,6 +1481,24 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { SyntaxParsingContext ExprContext(SyntaxContext, SyntaxContextKind::Expr); switch (Tok.getKind()) { case tok::at_sign: + { + // Try to parse as an attributed type. This is mainly for @qualified. + bool isType = false; + { + BacktrackingScope backtrack(*this); + isType = canParseType(); + } + if (isType) { + // Parse a TypeExpr, but make it the smallest we possibly can so we don't + // misinterpret references to decls as types instead. + ParserResult ty = parseType(ParseTypeFlags::Minimal); + if (ty.isNonNull()) { + auto E = new (Context) TypeExpr(TypeLoc(ty.get(), Type())); + return makeParserResult(static_cast(E)); + } + } + } + // Objective-C programmers habitually type @"foo", so recover gracefully // with a fixit. If this isn't @"foo", just handle it like an unknown // input. diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index 9cb81c42d6ee4..fb5d4a5d12d1d 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -337,7 +337,8 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc, } if (isBareType && paramContext == ParameterContextKind::EnumElement) { - auto type = parseType(diag::expected_parameter_type, false); + auto type = parseType(diag::expected_parameter_type, + ParseTypeFlags::IgnoreCodeCompletion); status |= type; param.Type = type.getPtrOrNull(); param.FirstName = Identifier(); @@ -348,7 +349,8 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc, // Otherwise, if this is a bare type, then the user forgot to name the // parameter, e.g. "func foo(Int) {}" SourceLoc typeStartLoc = Tok.getLoc(); - auto type = parseType(diag::expected_parameter_type, false); + auto type = parseType(diag::expected_parameter_type, + ParseTypeFlags::IgnoreCodeCompletion); status |= type; param.Type = type.getPtrOrNull(); diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index e12b01fbefe44..c633e8afe8f4d 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -151,7 +151,7 @@ LayoutConstraint Parser::parseLayoutConstraint(Identifier LayoutConstraintID) { /// type-collection /// type-array Parser::TypeResult Parser::parseTypeSimple(Diag<> MessageID, - bool HandleCodeCompletion) { + ParseTypeOptions Flags) { if (Tok.is(tok::kw_inout) || (Tok.is(tok::identifier) && (Tok.getRawText().equals("__shared") || Tok.getRawText().equals("__owned")))) { @@ -168,13 +168,13 @@ Parser::TypeResult Parser::parseTypeSimple(Diag<> MessageID, case tok::kw_Self: case tok::kw_Any: case tok::identifier: - Result = parseTypeIdentifier(); + Result = parseTypeIdentifier(Flags); break; case tok::l_paren: Result = parseTypeTupleBody(); break; case tok::code_complete: { - if (!HandleCodeCompletion) + if (Flags.contains(ParseTypeFlags::IgnoreCodeCompletion)) break; if (CodeCompletion) CodeCompletion->completeTypeSimpleBeginning(); @@ -240,8 +240,8 @@ Parser::TypeResult Parser::parseTypeSimple(Diag<> MessageID, return *Result; } -Parser::TypeASTResult Parser::parseType() { - return parseType(diag::expected_type); +Parser::TypeASTResult Parser::parseType(ParseTypeOptions Flags) { + return parseType(diag::expected_type, Flags); } Parser::TypeASTResult Parser::parseSILBoxType(GenericParamList *generics, @@ -325,8 +325,7 @@ Parser::TypeASTResult Parser::parseSILBoxType(GenericParamList *generics, /// type-composition 'throws'? '->' type /// Parser::TypeASTResult Parser::parseType(Diag<> MessageID, - bool HandleCodeCompletion, - bool IsSILFuncDecl) { + ParseTypeOptions Flags) { // Start a context for creating type syntax. SyntaxParsingContext TypeParsingContext(SyntaxContext, SyntaxContextKind::Type); @@ -345,7 +344,7 @@ Parser::TypeASTResult Parser::parseType(Diag<> MessageID, if (isInSILMode()) { // If this is part of a sil function decl, generic parameters are visible in // the function body; otherwise, they are visible when parsing the type. - if (!IsSILFuncDecl) + if (!Flags.contains(ParseTypeFlags::IsSILFuncDecl)) GenericsScope.emplace(this, ScopeKind::Generics); generics = maybeParseGenericParams().getPtrOrNull(); } @@ -360,7 +359,7 @@ Parser::TypeASTResult Parser::parseType(Diag<> MessageID, auto RealTypeLoc = leadingTriviaLoc(); ParserResult ty = - parseTypeSimpleOrCompositionAST(MessageID, HandleCodeCompletion); + parseTypeSimpleOrCompositionAST(MessageID, Flags); if (ty.hasCodeCompletion()) return makeParserCodeCompletionResult(); if (ty.isNull()) @@ -614,7 +613,7 @@ Parser::parseGenericArgumentsAST(SmallVectorImpl &ArgsAST, /// type-identifier: /// identifier generic-args? ('.' identifier generic-args?)* /// -Parser::TypeResult Parser::parseTypeIdentifier() { +Parser::TypeResult Parser::parseTypeIdentifier(Parser::ParseTypeOptions Flags) { if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_Self)) { // is this the 'Any' type if (Tok.is(tok::kw_Any)) @@ -702,6 +701,8 @@ Parser::TypeResult Parser::parseTypeIdentifier() { } if (!peekToken().isContextualKeyword("Type") && !peekToken().isContextualKeyword("Protocol")) { + if (Flags.contains(ParseTypeFlags::Minimal)) + break; Period = consumeTokenSyntax(); continue; } @@ -747,11 +748,11 @@ Parser::TypeResult Parser::parseTypeIdentifier() { Parser::TypeASTResult Parser::parseTypeSimpleOrCompositionAST(Diag<> MessageID, - bool HandleCodeCompletion) { + ParseTypeOptions Flags) { auto Loc = leadingTriviaLoc(); auto CompositionResult = - parseTypeSimpleOrComposition(MessageID, HandleCodeCompletion); + parseTypeSimpleOrComposition(MessageID, Flags); if (!CompositionResult.isSuccess()) { auto nodes = CompositionResult.getUnknownNodes(); @@ -786,7 +787,7 @@ Parser::parseTypeSimpleOrCompositionAST(Diag<> MessageID, /// type-composition '&' type-simple Parser::TypeResult Parser::parseTypeSimpleOrComposition(Diag<> MessageID, - bool HandleCodeCompletion) { + ParseTypeOptions Flags) { // Check for the opaque modifier. // This is only semantically allowed in certain contexts, but we parse it // generally for diagnostics and recovery. @@ -803,7 +804,7 @@ Parser::parseTypeSimpleOrComposition(Diag<> MessageID, }; // Parse the first type - auto FirstTypeResult = parseTypeSimple(MessageID, HandleCodeCompletion); + auto FirstTypeResult = parseTypeSimple(MessageID, Flags); // todo [gsoc]: handle Junk properly here if (!FirstTypeResult.isSuccess()) @@ -834,7 +835,7 @@ Parser::parseTypeSimpleOrComposition(Diag<> MessageID, } auto NextTypeResult = parseTypeSimple(diag::expected_identifier_for_type, - HandleCodeCompletion); + Flags); if (!NextTypeResult.isSuccess()) { auto following = NextTypeResult.getUnknownNodes(); diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index 8247c66b80e9e..8b9e4f0b5d220 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -1272,9 +1272,10 @@ bool SILParser::parseSILType(SILType &Result, attrs.convention = "thin"; } - ParserResult TyR = P.parseType(diag::expected_sil_type, - /*handleCodeCompletion*/ true, - /*isSILFuncDecl*/ IsFuncDecl); + Parser::ParseTypeOptions Flags; + if (IsFuncDecl) + Flags |= Parser::ParseTypeFlags::IsSILFuncDecl; + ParserResult TyR = P.parseType(diag::expected_sil_type, Flags); if (TyR.isNull()) return true; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 67430e45c618d..657abaa9c2855 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -939,6 +939,10 @@ namespace { /// The expressions that are direct arguments of call expressions. llvm::SmallPtrSet CallArgs; + /// Simplify TypeExprs which represent a fully-qualified module name only + /// into a DeclRefExpr. + DeclRefExpr *simplifyQualifiedModuleRef(Expr *TE); + /// Simplify expressions which are type sugar productions that got parsed /// as expressions due to the parser not knowing which identifiers are /// type names. @@ -1329,6 +1333,11 @@ namespace { return DAE; } + // Turn qualified references to modules into DeclRefExprs. + if (auto DRE = simplifyQualifiedModuleRef(expr)) { + return DRE; + } + // If this is a sugared type that needs to be folded into a single // TypeExpr, do it. if (auto *simplified = simplifyTypeExpr(expr)) @@ -1397,6 +1406,36 @@ bool PreCheckExpression::walkToClosureExprPre(ClosureExpr *closure) { return true; } +static TypeRepr *skipParens(TypeRepr *TR) { + assert(TR); + while (isa(TR) && cast(TR)->isParenType()) + TR = cast(TR)->getElement(0).Type; + return TR; +} + +DeclRefExpr *PreCheckExpression::simplifyQualifiedModuleRef(Expr *E) { + auto TE = dyn_cast(E); + if (!TE || !TE->getTypeRepr()) return nullptr; + + TypeRepr * Repr = skipParens(TE->getTypeRepr()); + + auto ATR = dyn_cast(Repr); + if (!ATR || !ATR->getAttrs().has(TAK_qualified)) return nullptr; + + Repr = skipParens(ATR->getTypeRepr()); + + auto SITR = dyn_cast(Repr); + if (!SITR) return nullptr; + + auto modules = TC.lookupUnqualified(DC, SITR->getIdentifier(), SITR->getLoc(), + NameLookupFlags::IncludeOnlyModules); + if (modules.size() != 1) return nullptr; + + ConcreteDeclRef concreteRef(modules.front().getValueDecl()); + return new (TC.Context) DeclRefExpr(concreteRef, DeclNameLoc(TE->getLoc()), + /*Implicit=*/false); +} + TypeExpr *PreCheckExpression::simplifyNestedTypeExpr(UnresolvedDotExpr *UDE) { if (!UDE->getName().isSimpleName() || UDE->getName().isSpecial()) diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index dc7979df10424..ac1bc291050db 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -260,6 +260,8 @@ convertToUnqualifiedLookupOptions(NameLookupOptions options) { newOptions |= UnqualifiedLookup::Flags::IgnoreAccessControl; if (options.contains(NameLookupFlags::IncludeOuterResults)) newOptions |= UnqualifiedLookup::Flags::IncludeOuterResults; + if (options.contains(NameLookupFlags::IncludeOnlyModules)) + newOptions |= UnqualifiedLookup::Flags::IncludeOnlyModules; return newOptions; } diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 4f282f0a3d6c9..7336c760113e3 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -656,6 +656,7 @@ adjustOptionsForGenericArgs(TypeResolutionOptions options) { options.setContext(None); options -= TypeResolutionFlags::SILType; options -= TypeResolutionFlags::AllowUnavailableProtocol; + options -= TypeResolutionFlags::FullyQualified; return options; } @@ -1325,6 +1326,9 @@ resolveTopLevelIdentTypeComponent(TypeResolution resolution, NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; if (options.contains(TypeResolutionFlags::KnownNonCascadingDependency)) lookupOptions |= NameLookupFlags::KnownPrivate; + if (options.contains(TypeResolutionFlags::FullyQualified)) + lookupOptions |= NameLookupFlags::IncludeOnlyModules; + auto globals = TypeChecker::lookupUnqualifiedType(DC, id, comp->getIdLoc(), @@ -2057,6 +2061,12 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // The type we're working with, in case we want to build it differently // based on the attributes we see. Type ty; + + if (attrs.has(TAK_qualified)) { + TypeResolutionOptions innerOptions = options; + innerOptions |= TypeResolutionFlags::FullyQualified; + return resolveType(repr, innerOptions); + } // If this is a reference to an opaque return type, resolve it. if (auto &opaque = attrs.OpaqueReturnTypeOf) { diff --git a/lib/Sema/TypeCheckType.h b/lib/Sema/TypeCheckType.h index d4fb21e2f363e..c5d0531167996 100644 --- a/lib/Sema/TypeCheckType.h +++ b/lib/Sema/TypeCheckType.h @@ -66,6 +66,9 @@ enum class TypeResolutionFlags : uint16_t { /// Whether we should not produce diagnostics if the type is invalid. SilenceErrors = 1 << 10, + + /// Whether the name is known to be fully qualified. + FullyQualified = 1 << 11, }; /// Type resolution contexts that require special handling. diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index b48958302f113..532cc0ceae7ed 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -312,6 +312,9 @@ enum class NameLookupFlags { IncludeOuterResults = 0x20, /// Whether to consider synonyms declared through @_implements(). IncludeAttributeImplements = 0x40, + /// Whether to only look up modules visible at this location, not other kinds + /// of visible names. + IncludeOnlyModules = 0x80, }; /// A set of options that control name lookup. diff --git a/test/attr/attr_qualified.swift b/test/attr/attr_qualified.swift new file mode 100644 index 0000000000000..26b69a2c08fb5 --- /dev/null +++ b/test/attr/attr_qualified.swift @@ -0,0 +1,17 @@ +// RUN: %target-typecheck-verify-swift + +class Swift { + struct Nested {} +} + +func badType() -> Swift.Int { 0 } // expected-error {{'Int' is not a member type of 'Swift'}} +func goodType() -> @qualified Swift.Int { 0 } // no-error +func uglyType() -> @qualified Swift.Array { [] } // no-error + +func badInit() -> Int { Swift.Int(bitPattern: 0) } // expected-error {{type 'Swift' has no member 'Int'}} +func goodInit() -> Int { @qualified Swift.Int(bitPattern: 0) } // no-error +func uglyInit() -> Int { (@qualified Swift.Int)(bitPattern: 0) } // no-error + +func badRef() { Swift.print("hello") } // expected-error {{type 'Swift' has no member 'print'}} +func goodRef() { @qualified Swift.print("hello") } // no-error +func uglyRef() { (@qualified Swift.print)("hello") } // no-error