Files
swift-mirror/include/swift/AST/Initializer.h
Doug Gregor ee9c066050 Introduce a new Initializer subclass for the arguments of custom attributes
Since the introduction of custom attributes (as part of property
wrappers), we've modeled the context of expressions within these
attributes as PatternBindingInitializers. These
PatternBindingInitializers would get wired in to the variable
declarations they apply to, establishing the appropriate declaration
context hierarchy. This worked because property wrappers only every
applied to---you guessed it!---properties, so the
PatternBindingInitializer would always get filled in.

When custom attributes were extended to apply to anything for the
purposes of macros, the use of PatternBindingInitializer became less
appropriate. Specifically, the binding declaration would never get
filled in (it's always NULL), so any place in the compiler that
accesses the binding might have to deal with it being NULL, which is a
new requirement. Few did, crashes ensued.

Rather than continue to play whack-a-mole with the abused
PatternBindingInitializer, introduce a new CustomAttributeInitializer
to model the context of custom attribute arguments. When the
attributes are assigned to a declaration that has a
PatternBindingInitializer, we reparent this new initializer to the
PatternBindingInitializer. This helps separate out the logic for
custom attributes vs. actual initializers.

Fixes https://github.com/swiftlang/swift/issues/76409 / rdar://136997841
2024-12-06 17:40:32 -08:00

217 lines
6.5 KiB
C++

//===--- Initializer.h - Initializer DeclContext ----------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the Initializer class, which is a kind of
// DeclContext used for expressions that are not part of a normal
// code-evaluation context, such as a global initializer or a default
// argument.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_INITIALIZER_H
#define SWIFT_INITIALIZER_H
#include "swift/AST/DeclContext.h"
namespace llvm {
class raw_ostream;
}
namespace swift {
class ParamDecl;
class PatternBindingDecl;
enum class InitializerKind : uint8_t {
/// The initializer expression of a PatternBindingDecl that declares
/// a global variable or type member.
PatternBinding,
/// A function's default argument expression.
DefaultArgument,
/// A property wrapper initialization expression.
PropertyWrapper,
/// An expression within a custom attribute.
CustomAttribute,
};
/// An Initializer is a kind of DeclContext used for expressions that
/// aren't potentially evaluated as part of some function.
///
/// Generally, Initializers are created lazily, as most initializers
/// don't really require DeclContexts.
class Initializer : public DeclContext {
unsigned Kind : 2;
protected:
unsigned SpareBits : 30;
Initializer(InitializerKind kind, DeclContext *parent)
: DeclContext(DeclContextKind::Initializer, parent),
Kind(unsigned(kind)) {
}
// Expose this to subclasses.
using DeclContext::setParent;
public:
/// Returns the kind of initializer this is.
InitializerKind getInitializerKind() const {
return InitializerKind(Kind);
}
static bool classof(const DeclContext *DC) {
return DC->getContextKind() == DeclContextKind::Initializer;
}
static bool classof(const Initializer *I) { return true; }
};
/// The initializer expression of a non-local pattern binding
/// declaration, such as a field or global variable.
class PatternBindingInitializer : public Initializer {
PatternBindingDecl *Binding;
// created lazily for 'self' lookup from lazy property initializer
ParamDecl *SelfParam;
// Sets itself as the parent.
friend class PatternBindingDecl;
void setBinding(PatternBindingDecl *binding, unsigned bindingIndex);
explicit PatternBindingInitializer(DeclContext *parent)
: Initializer(InitializerKind::PatternBinding, parent),
Binding(nullptr), SelfParam(nullptr) {
SpareBits = 0;
}
public:
static PatternBindingInitializer *create(DeclContext *parent) {
return new (parent->getASTContext()) PatternBindingInitializer(parent);
}
static PatternBindingInitializer *createDeserialized(PatternBindingDecl *PBD,
unsigned index);
PatternBindingDecl *getBinding() const { return Binding; }
unsigned getBindingIndex() const { return SpareBits; }
/// If this initializes a single @lazy variable, return it.
VarDecl *getInitializedLazyVar() const;
/// If this initializes a single @lazy variable, lazily create a self
/// declaration for it to refer to.
ParamDecl *getImplicitSelfDecl() const;
static bool classof(const DeclContext *DC) {
if (auto init = dyn_cast<Initializer>(DC))
return classof(init);
return false;
}
static bool classof(const Initializer *I) {
return I->getInitializerKind() == InitializerKind::PatternBinding;
}
};
/// A default argument expression. The parent context is the function
/// (possibly a closure) for which this is a default argument.
class DefaultArgumentInitializer : public Initializer {
public:
explicit DefaultArgumentInitializer(DeclContext *parent, unsigned index)
: Initializer(InitializerKind::DefaultArgument, parent) {
SpareBits = index;
}
unsigned getIndex() const { return SpareBits; }
/// Change the parent of this context. This is necessary because
/// the function signature is parsed before the function
/// declaration/expression itself is built.
void changeFunction(DeclContext *parent, ParameterList *paramLists);
static bool classof(const DeclContext *DC) {
if (auto init = dyn_cast<Initializer>(DC))
return classof(init);
return false;
}
static bool classof(const Initializer *I) {
return I->getInitializerKind() == InitializerKind::DefaultArgument;
}
};
/// A property wrapper initialization expression. The parent context is the
/// function or closure which owns the property wrapper.
class PropertyWrapperInitializer : public Initializer {
public:
enum class Kind {
WrappedValue,
ProjectedValue
};
private:
VarDecl *wrappedVar;
Kind kind;
public:
explicit PropertyWrapperInitializer(DeclContext *parent, VarDecl *wrappedVar,
Kind kind)
: Initializer(InitializerKind::PropertyWrapper, parent),
wrappedVar(wrappedVar), kind(kind) {}
VarDecl *getWrappedVar() const { return wrappedVar; }
Kind getKind() const { return kind; }
static bool classof(const DeclContext *DC) {
if (auto init = dyn_cast<Initializer>(DC))
return classof(init);
return false;
}
static bool classof(const Initializer *I) {
return I->getInitializerKind() == InitializerKind::PropertyWrapper;
}
};
/// An expression within a custom attribute. The parent context is the
/// context in which the attributed declaration occurs.
class CustomAttributeInitializer : public Initializer {
public:
explicit CustomAttributeInitializer(DeclContext *parent)
: Initializer(InitializerKind::CustomAttribute, parent) {}
static CustomAttributeInitializer *create(DeclContext *parent) {
return new (parent->getASTContext()) CustomAttributeInitializer(parent);
}
void setEnclosingInitializer(Initializer *newParent) {
setParent(newParent);
}
static bool classof(const DeclContext *DC) {
if (auto init = dyn_cast<Initializer>(DC))
return classof(init);
return false;
}
static bool classof(const Initializer *I) {
return I->getInitializerKind() == InitializerKind::CustomAttribute;
}
};
void simple_display(llvm::raw_ostream &out, Initializer *init);
} // end namespace swift
#endif